SF: dispatch add testing surface for VSyncDispatch

Adds a stubbable/mockable surface to the VSyncDispatch class so that
an test relying on this object can be built.

Bug: 140303479
Test: libsurfaceflinger_unittest --gtest_filter="VSyncDisp*"

Change-Id: I7e0a6b14e7032c89c32595a624eef4f75e1ea1ad
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
new file mode 100644
index 0000000..7922484
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+#include <vector>
+
+#include "TimeKeeper.h"
+#include "VSyncDispatchTimerQueue.h"
+#include "VSyncTracker.h"
+
+namespace android::scheduler {
+
+VSyncDispatch::~VSyncDispatch() = default;
+VSyncTracker::~VSyncTracker() = default;
+TimeKeeper::~TimeKeeper() = default;
+
+VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string const& name,
+                                                           std::function<void(nsecs_t)> const& cb)
+      : mName(name), mCallback(cb), mWorkDuration(0), mEarliestVsync(0) {}
+
+std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
+    return mLastDispatchTime;
+}
+
+std::string_view VSyncDispatchTimerQueueEntry::name() const {
+    return mName;
+}
+
+std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::wakeupTime() const {
+    if (!mArmedInfo) {
+        return {};
+    }
+    return {mArmedInfo->mActualWakeupTime};
+}
+
+nsecs_t VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
+                                               VSyncTracker& tracker, nsecs_t now) {
+    mWorkDuration = workDuration;
+    mEarliestVsync = earliestVsync;
+    arm(tracker, now);
+    return mArmedInfo->mActualWakeupTime;
+}
+
+void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
+    if (!mArmedInfo) {
+        return;
+    }
+    arm(tracker, now);
+}
+
+void VSyncDispatchTimerQueueEntry::arm(VSyncTracker& tracker, nsecs_t now) {
+    auto const nextVsyncTime =
+            tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration));
+    mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime};
+}
+
+void VSyncDispatchTimerQueueEntry::disarm() {
+    mArmedInfo.reset();
+}
+
+nsecs_t VSyncDispatchTimerQueueEntry::executing() {
+    mLastDispatchTime = mArmedInfo->mActualVsyncTime;
+    disarm();
+    return *mLastDispatchTime;
+}
+
+void VSyncDispatchTimerQueueEntry::callback(nsecs_t t) {
+    {
+        std::lock_guard<std::mutex> lk(mRunningMutex);
+        mRunning = true;
+    }
+
+    mCallback(t);
+
+    std::lock_guard<std::mutex> lk(mRunningMutex);
+    mRunning = false;
+    mCv.notify_all();
+}
+
+void VSyncDispatchTimerQueueEntry::ensureNotRunning() {
+    std::unique_lock<std::mutex> lk(mRunningMutex);
+    mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; });
+}
+
+VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
+                                                 VSyncTracker& tracker, nsecs_t timerSlack)
+      : mTimeKeeper(std::move(tk)), mTracker(tracker), mTimerSlack(timerSlack) {}
+
+VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    cancelTimer();
+}
+
+void VSyncDispatchTimerQueue::cancelTimer() {
+    mIntendedWakeupTime = kInvalidTime;
+    mTimeKeeper->alarmCancel();
+}
+
+void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t now) {
+    mIntendedWakeupTime = targetTime;
+    mTimeKeeper->alarmIn(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
+                         targetTime - now);
+}
+
+void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) {
+    rearmTimerSkippingUpdateFor(now, mCallbacks.end());
+}
+
+void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
+        nsecs_t now, CallbackMap::iterator const& skipUpdateIt) {
+    std::optional<nsecs_t> min;
+    for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
+        auto& callback = it->second;
+        if (!callback->wakeupTime()) {
+            continue;
+        }
+
+        if (it != skipUpdateIt) {
+            callback->update(mTracker, now);
+        }
+        auto const wakeupTime = *callback->wakeupTime();
+        if (!min || (min && *min > wakeupTime)) {
+            min = wakeupTime;
+        }
+    }
+
+    if (min && (min < mIntendedWakeupTime)) {
+        setTimer(*min, now);
+    } else {
+        cancelTimer();
+    }
+}
+
+void VSyncDispatchTimerQueue::timerCallback() {
+    struct Invocation {
+        std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
+        nsecs_t timestamp;
+    };
+    std::vector<Invocation> invocations;
+    {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
+            auto& callback = it->second;
+            auto const wakeupTime = callback->wakeupTime();
+            if (!wakeupTime) {
+                continue;
+            }
+
+            if (*wakeupTime < mIntendedWakeupTime + mTimerSlack) {
+                callback->executing();
+                invocations.emplace_back(
+                        Invocation{callback, *callback->lastExecutedVsyncTarget()});
+            }
+        }
+
+        mIntendedWakeupTime = kInvalidTime;
+        rearmTimer(mTimeKeeper->now());
+    }
+
+    for (auto const& invocation : invocations) {
+        invocation.callback->callback(invocation.timestamp);
+    }
+}
+
+VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
+        std::function<void(nsecs_t)> const& callbackFn, std::string callbackName) {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    return CallbackToken{
+            mCallbacks
+                    .emplace(++mCallbackToken,
+                             std::make_shared<VSyncDispatchTimerQueueEntry>(callbackName,
+                                                                            callbackFn))
+                    .first->first};
+}
+
+void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
+    std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr;
+    {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        auto it = mCallbacks.find(token);
+        if (it != mCallbacks.end()) {
+            entry = it->second;
+            mCallbacks.erase(it);
+        }
+    }
+
+    if (entry) {
+        entry->ensureNotRunning();
+    }
+}
+
+ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t workDuration,
+                                                 nsecs_t earliestVsync) {
+    auto result = ScheduleResult::Error;
+    {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+
+        auto it = mCallbacks.find(token);
+        if (it == mCallbacks.end()) {
+            return result;
+        }
+        auto& callback = it->second;
+        result = callback->wakeupTime() ? ScheduleResult::ReScheduled : ScheduleResult::Scheduled;
+
+        auto const now = mTimeKeeper->now();
+        auto const wakeupTime = callback->schedule(workDuration, earliestVsync, mTracker, now);
+
+        if (wakeupTime < now - mTimerSlack || callback->lastExecutedVsyncTarget() > wakeupTime) {
+            return ScheduleResult::CannotSchedule;
+        }
+
+        if (wakeupTime < mIntendedWakeupTime - mTimerSlack) {
+            rearmTimerSkippingUpdateFor(now, it);
+        }
+    }
+
+    return result;
+}
+
+CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+
+    auto it = mCallbacks.find(token);
+    if (it == mCallbacks.end()) {
+        return CancelResult::Error;
+    }
+    auto& callback = it->second;
+
+    if (callback->wakeupTime()) {
+        callback->disarm();
+        mIntendedWakeupTime = kInvalidTime;
+        rearmTimer(mTimeKeeper->now());
+        return CancelResult::Cancelled;
+    }
+    return CancelResult::TooLate;
+}
+
+VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
+                                                     std::function<void(nsecs_t)> const& callbackFn,
+                                                     std::string const& callbackName)
+      : mDispatch(dispatch),
+        mToken(dispatch.registerCallback(callbackFn, callbackName)),
+        mValidToken(true) {}
+
+VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other)
+      : mDispatch(other.mDispatch),
+        mToken(std::move(other.mToken)),
+        mValidToken(std::move(other.mValidToken)) {
+    other.mValidToken = false;
+}
+
+VSyncCallbackRegistration& VSyncCallbackRegistration::operator=(VSyncCallbackRegistration&& other) {
+    mDispatch = std::move(other.mDispatch);
+    mToken = std::move(other.mToken);
+    mValidToken = std::move(other.mValidToken);
+    other.mValidToken = false;
+    return *this;
+}
+
+VSyncCallbackRegistration::~VSyncCallbackRegistration() {
+    if (mValidToken) mDispatch.get().unregisterCallback(mToken);
+}
+
+ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) {
+    if (!mValidToken) return ScheduleResult::Error;
+    return mDispatch.get().schedule(mToken, workDuration, earliestVsync);
+}
+
+CancelResult VSyncCallbackRegistration::cancel() {
+    if (!mValidToken) return CancelResult::Error;
+    return mDispatch.get().cancel(mToken);
+}
+
+} // namespace android::scheduler