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/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 0050495..4a4bef8 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -16,14 +16,9 @@
#pragma once
-#include <android-base/thread_annotations.h>
#include <utils/Timers.h>
#include <functional>
-#include <memory>
-#include <mutex>
#include <string>
-#include <string_view>
-#include <unordered_map>
#include "StrongTyping.h"
@@ -34,68 +29,6 @@
enum class ScheduleResult { Scheduled, ReScheduled, CannotSchedule, Error };
enum class CancelResult { Cancelled, TooLate, Error };
-namespace impl {
-
-// VSyncDispatchEntry is a helper class representing internal state for each entry in VSyncDispatch
-// hoisted to public for unit testing.
-class VSyncDispatchEntry {
-public:
- // This is the state of the entry. There are 3 states, armed, running, disarmed.
- // Valid transition: disarmed -> armed ( when scheduled )
- // Valid transition: armed -> running -> disarmed ( when timer is called)
- // Valid transition: armed -> disarmed ( when cancelled )
- VSyncDispatchEntry(std::string const& name, std::function<void(nsecs_t)> const& fn);
- std::string_view name() const;
-
- // Start: functions that are not threadsafe.
- // Return the last vsync time this callback was invoked.
- std::optional<nsecs_t> lastExecutedVsyncTarget() const;
-
- // This moves the state from disarmed->armed and will calculate the wakeupTime.
- nsecs_t schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
- nsecs_t now);
- // This will update armed entries with the latest vsync information. Entry remains armed.
- void update(VSyncTracker& tracker, nsecs_t now);
-
- // This will return empty if not armed, or the next calculated wakeup time if armed.
- // It will not update the wakeupTime.
- std::optional<nsecs_t> wakeupTime() const;
-
- // This moves state from armed->disarmed.
- void disarm();
-
- // This moves the state from armed->running.
- // Store the timestamp that this was intended for as the last called timestamp.
- nsecs_t executing();
- // End: functions that are not threadsafe.
-
- // Invoke the callback with the timestamp, moving the state from running->disarmed.
- void callback(nsecs_t timestamp);
- // Block calling thread while the callback is executing.
- void ensureNotRunning();
-
-private:
- void arm(VSyncTracker& tracker, nsecs_t now);
- std::string const mName;
- std::function<void(nsecs_t)> const mCallback;
-
- nsecs_t mWorkDuration;
- nsecs_t mEarliestVsync;
-
- struct ArmingInfo {
- nsecs_t mActualWakeupTime;
- nsecs_t mActualVsyncTime;
- };
- std::optional<ArmingInfo> mArmedInfo;
- std::optional<nsecs_t> mLastDispatchTime;
-
- std::mutex mRunningMutex;
- std::condition_variable mCv;
- bool mRunning GUARDED_BY(mRunningMutex) = false;
-};
-
-} // namespace impl
-
/*
* VSyncDispatch is a class that will dispatch callbacks relative to system vsync events.
*/
@@ -103,15 +36,7 @@
public:
using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare>;
- /* creates a VsyncDispatch.
- * \param [in] a timekeeper object for dispatching events.
- * \param [in] a tracker object that is monitoring expected vsync events.
- * \param [in] a tunable in nanoseconds that indicates when events that fall close together
- * should be dispatched in one timer wakeup.
- */
- explicit VSyncDispatch(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
- nsecs_t timerSlack);
- ~VSyncDispatch();
+ virtual ~VSyncDispatch();
/*
* Registers a callback that will be called at designated points on the vsync timeline.
@@ -126,8 +51,8 @@
* invocation of callbackFn.
*
*/
- CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
- std::string callbackName);
+ virtual CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
+ std::string callbackName) = 0;
/*
* Unregisters a callback.
@@ -135,7 +60,7 @@
* \param [in] token The callback to unregister.
*
*/
- void unregisterCallback(CallbackToken token);
+ virtual void unregisterCallback(CallbackToken token) = 0;
/*
* Schedules the registered callback to be dispatched.
@@ -164,7 +89,8 @@
* if a callback was dispatched for the predictedVsync already.
* A ScheduleResult::Error if there was another error.
*/
- ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync);
+ virtual ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
+ nsecs_t earliestVsync) = 0;
/* Cancels a scheduled callback, if possible.
*
@@ -173,31 +99,12 @@
* A CancelResult::Cancelled if the callback was successfully cancelled.
* A CancelResult::Error if there was an pre-condition violation.
*/
- CancelResult cancel(CallbackToken token);
+ virtual CancelResult cancel(CallbackToken token) = 0;
-private:
+protected:
+ VSyncDispatch() = default;
VSyncDispatch(VSyncDispatch const&) = delete;
VSyncDispatch& operator=(VSyncDispatch const&) = delete;
-
- using CallbackMap = std::unordered_map<size_t, std::shared_ptr<impl::VSyncDispatchEntry>>;
-
- void timerCallback();
- void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex);
- void rearmTimer(nsecs_t now) REQUIRES(mMutex);
- void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate)
- REQUIRES(mMutex);
- void cancelTimer() REQUIRES(mMutex);
-
- static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
- std::unique_ptr<TimeKeeper> const mTimeKeeper;
- VSyncTracker& mTracker;
- nsecs_t const mTimerSlack;
-
- std::mutex mutable mMutex;
- size_t mCallbackToken GUARDED_BY(mMutex) = 0;
-
- CallbackMap mCallbacks GUARDED_BY(mMutex);
- nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime;
};
/*
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
similarity index 74%
rename from services/surfaceflinger/Scheduler/VSyncDispatch.cpp
rename to services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index c9b2e77..7922484 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -19,65 +19,66 @@
#include <vector>
#include "TimeKeeper.h"
-#include "VSyncDispatch.h"
+#include "VSyncDispatchTimerQueue.h"
#include "VSyncTracker.h"
namespace android::scheduler {
+VSyncDispatch::~VSyncDispatch() = default;
VSyncTracker::~VSyncTracker() = default;
TimeKeeper::~TimeKeeper() = default;
-impl::VSyncDispatchEntry::VSyncDispatchEntry(std::string const& name,
- std::function<void(nsecs_t)> const& cb)
+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> impl::VSyncDispatchEntry::lastExecutedVsyncTarget() const {
+std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
return mLastDispatchTime;
}
-std::string_view impl::VSyncDispatchEntry::name() const {
+std::string_view VSyncDispatchTimerQueueEntry::name() const {
return mName;
}
-std::optional<nsecs_t> impl::VSyncDispatchEntry::wakeupTime() const {
+std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::wakeupTime() const {
if (!mArmedInfo) {
return {};
}
return {mArmedInfo->mActualWakeupTime};
}
-nsecs_t impl::VSyncDispatchEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
- VSyncTracker& tracker, nsecs_t now) {
+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 impl::VSyncDispatchEntry::update(VSyncTracker& tracker, nsecs_t now) {
+void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
if (!mArmedInfo) {
return;
}
arm(tracker, now);
}
-void impl::VSyncDispatchEntry::arm(VSyncTracker& tracker, nsecs_t now) {
+void VSyncDispatchTimerQueueEntry::arm(VSyncTracker& tracker, nsecs_t now) {
auto const nextVsyncTime =
tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration));
mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime};
}
-void impl::VSyncDispatchEntry::disarm() {
+void VSyncDispatchTimerQueueEntry::disarm() {
mArmedInfo.reset();
}
-nsecs_t impl::VSyncDispatchEntry::executing() {
+nsecs_t VSyncDispatchTimerQueueEntry::executing() {
mLastDispatchTime = mArmedInfo->mActualVsyncTime;
disarm();
return *mLastDispatchTime;
}
-void impl::VSyncDispatchEntry::callback(nsecs_t t) {
+void VSyncDispatchTimerQueueEntry::callback(nsecs_t t) {
{
std::lock_guard<std::mutex> lk(mRunningMutex);
mRunning = true;
@@ -90,36 +91,37 @@
mCv.notify_all();
}
-void impl::VSyncDispatchEntry::ensureNotRunning() {
+void VSyncDispatchTimerQueueEntry::ensureNotRunning() {
std::unique_lock<std::mutex> lk(mRunningMutex);
mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; });
}
-VSyncDispatch::VSyncDispatch(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
- nsecs_t timerSlack)
+VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
+ VSyncTracker& tracker, nsecs_t timerSlack)
: mTimeKeeper(std::move(tk)), mTracker(tracker), mTimerSlack(timerSlack) {}
-VSyncDispatch::~VSyncDispatch() {
+VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
std::lock_guard<decltype(mMutex)> lk(mMutex);
cancelTimer();
}
-void VSyncDispatch::cancelTimer() {
+void VSyncDispatchTimerQueue::cancelTimer() {
mIntendedWakeupTime = kInvalidTime;
mTimeKeeper->alarmCancel();
}
-void VSyncDispatch::setTimer(nsecs_t targetTime, nsecs_t now) {
+void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t now) {
mIntendedWakeupTime = targetTime;
- mTimeKeeper->alarmIn(std::bind(&VSyncDispatch::timerCallback, this), targetTime - now);
+ mTimeKeeper->alarmIn(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
+ targetTime - now);
}
-void VSyncDispatch::rearmTimer(nsecs_t now) {
+void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) {
rearmTimerSkippingUpdateFor(now, mCallbacks.end());
}
-void VSyncDispatch::rearmTimerSkippingUpdateFor(nsecs_t now,
- CallbackMap::iterator const& skipUpdateIt) {
+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;
@@ -143,9 +145,9 @@
}
}
-void VSyncDispatch::timerCallback() {
+void VSyncDispatchTimerQueue::timerCallback() {
struct Invocation {
- std::shared_ptr<impl::VSyncDispatchEntry> callback;
+ std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
nsecs_t timestamp;
};
std::vector<Invocation> invocations;
@@ -174,18 +176,19 @@
}
}
-VSyncDispatch::CallbackToken VSyncDispatch::registerCallback(
+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<impl::VSyncDispatchEntry>(callbackName, callbackFn))
+ std::make_shared<VSyncDispatchTimerQueueEntry>(callbackName,
+ callbackFn))
.first->first};
}
-void VSyncDispatch::unregisterCallback(CallbackToken token) {
- std::shared_ptr<impl::VSyncDispatchEntry> entry = nullptr;
+void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
+ std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr;
{
std::lock_guard<decltype(mMutex)> lk(mMutex);
auto it = mCallbacks.find(token);
@@ -200,8 +203,8 @@
}
}
-ScheduleResult VSyncDispatch::schedule(CallbackToken token, nsecs_t workDuration,
- nsecs_t earliestVsync) {
+ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t workDuration,
+ nsecs_t earliestVsync) {
auto result = ScheduleResult::Error;
{
std::lock_guard<decltype(mMutex)> lk(mMutex);
@@ -228,7 +231,7 @@
return result;
}
-CancelResult VSyncDispatch::cancel(CallbackToken token) {
+CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
std::lock_guard<decltype(mMutex)> lk(mMutex);
auto it = mCallbacks.find(token);
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
new file mode 100644
index 0000000..f058099
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+
+#include "VSyncDispatch.h"
+
+namespace android::scheduler {
+
+// VSyncDispatchTimerQueueEntry is a helper class representing internal state for each entry in
+// VSyncDispatchTimerQueue hoisted to public for unit testing.
+class VSyncDispatchTimerQueueEntry {
+public:
+ // This is the state of the entry. There are 3 states, armed, running, disarmed.
+ // Valid transition: disarmed -> armed ( when scheduled )
+ // Valid transition: armed -> running -> disarmed ( when timer is called)
+ // Valid transition: armed -> disarmed ( when cancelled )
+ VSyncDispatchTimerQueueEntry(std::string const& name, std::function<void(nsecs_t)> const& fn);
+ std::string_view name() const;
+
+ // Start: functions that are not threadsafe.
+ // Return the last vsync time this callback was invoked.
+ std::optional<nsecs_t> lastExecutedVsyncTarget() const;
+
+ // This moves the state from disarmed->armed and will calculate the wakeupTime.
+ nsecs_t schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
+ nsecs_t now);
+ // This will update armed entries with the latest vsync information. Entry remains armed.
+ void update(VSyncTracker& tracker, nsecs_t now);
+
+ // This will return empty if not armed, or the next calculated wakeup time if armed.
+ // It will not update the wakeupTime.
+ std::optional<nsecs_t> wakeupTime() const;
+
+ // This moves state from armed->disarmed.
+ void disarm();
+
+ // This moves the state from armed->running.
+ // Store the timestamp that this was intended for as the last called timestamp.
+ nsecs_t executing();
+ // End: functions that are not threadsafe.
+
+ // Invoke the callback with the timestamp, moving the state from running->disarmed.
+ void callback(nsecs_t timestamp);
+ // Block calling thread while the callback is executing.
+ void ensureNotRunning();
+
+private:
+ void arm(VSyncTracker& tracker, nsecs_t now);
+ std::string const mName;
+ std::function<void(nsecs_t)> const mCallback;
+
+ nsecs_t mWorkDuration;
+ nsecs_t mEarliestVsync;
+
+ struct ArmingInfo {
+ nsecs_t mActualWakeupTime;
+ nsecs_t mActualVsyncTime;
+ };
+ std::optional<ArmingInfo> mArmedInfo;
+ std::optional<nsecs_t> mLastDispatchTime;
+
+ std::mutex mRunningMutex;
+ std::condition_variable mCv;
+ bool mRunning GUARDED_BY(mRunningMutex) = false;
+};
+
+/*
+ * VSyncDispatchTimerQueue is a class that will dispatch callbacks as per VSyncDispatch interface
+ * using a single timer queue.
+ */
+class VSyncDispatchTimerQueue : public VSyncDispatch {
+public:
+ explicit VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
+ nsecs_t timerSlack);
+ ~VSyncDispatchTimerQueue();
+
+ CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
+ std::string callbackName) final;
+ void unregisterCallback(CallbackToken token) final;
+ ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final;
+ CancelResult cancel(CallbackToken token) final;
+
+private:
+ VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete;
+ VSyncDispatchTimerQueue& operator=(VSyncDispatchTimerQueue const&) = delete;
+
+ using CallbackMap = std::unordered_map<size_t, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;
+
+ void timerCallback();
+ void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex);
+ void rearmTimer(nsecs_t now) REQUIRES(mMutex);
+ void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate)
+ REQUIRES(mMutex);
+ void cancelTimer() REQUIRES(mMutex);
+
+ static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
+ std::unique_ptr<TimeKeeper> const mTimeKeeper;
+ VSyncTracker& mTracker;
+ nsecs_t const mTimerSlack;
+
+ std::mutex mutable mMutex;
+ size_t mCallbackToken GUARDED_BY(mMutex) = 0;
+
+ CallbackMap mCallbacks GUARDED_BY(mMutex);
+ nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime;
+};
+
+} // namespace android::scheduler