Make VSyncCallbackRegistration's move operator= call unregisterCallback
Skipping unregisterCallback means that we might delete a callback while
it is being called. Note that EventThread uses the move operator=, and
this behaves differently than if it were stored in a unique_ptr like in
MessageQueue. Update move operator= so that they do behave the same, and
add tests verifying this, along with a couple other behavior tests.
Note that this may result in deadlocks similar to those in b/276367387.
That is fixed by If490f88115aa298d31aa9ad392a1f9f9dc987549.
Cleanups:
- Remove comment for VsyncCallbackRegistration regarding the lifetime
requirement for the VSyncDispatch.
Icdb80253436b4d0034fc20fcae8583efb7c30292 switched the VSyncDispatch
to an std::shared_ptr from a reference, so the client no longer needs
to ensure that the VSyncDispatch outlives the VsyncCallbackRegistration.
- Replace mValidToken with using an std::optional for the token.
Bug: 279209321
Test: VSyncCallbackRegistrationTest
Change-Id: I3c1eccb36914f29560600d48bb08b1b8f2fe7c96
diff --git a/services/surfaceflinger/tests/unittests/VSyncCallbackRegistrationTest.cpp b/services/surfaceflinger/tests/unittests/VSyncCallbackRegistrationTest.cpp
new file mode 100644
index 0000000..69b3861
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncCallbackRegistrationTest.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "Scheduler/VSyncDispatch.h"
+#include "mock/MockVSyncDispatch.h"
+
+using namespace testing;
+
+namespace android::scheduler {
+
+class VSyncCallbackRegistrationTest : public Test {
+protected:
+ VSyncDispatch::Callback mCallback = [](nsecs_t, nsecs_t, nsecs_t) {};
+
+ std::shared_ptr<mock::VSyncDispatch> mVsyncDispatch = std::make_shared<mock::VSyncDispatch>();
+ VSyncDispatch::CallbackToken mCallbackToken{7};
+ std::string mCallbackName = "callback";
+
+ std::shared_ptr<mock::VSyncDispatch> mVsyncDispatch2 = std::make_shared<mock::VSyncDispatch>();
+ VSyncDispatch::CallbackToken mCallbackToken2{42};
+ std::string mCallbackName2 = "callback2";
+
+ void assertDispatch(const VSyncCallbackRegistration& registration,
+ std::shared_ptr<VSyncDispatch> dispatch) {
+ ASSERT_EQ(registration.mDispatch, dispatch);
+ }
+
+ void assertToken(const VSyncCallbackRegistration& registration,
+ const std::optional<VSyncDispatch::CallbackToken>& token) {
+ ASSERT_EQ(registration.mToken, token);
+ }
+};
+
+TEST_F(VSyncCallbackRegistrationTest, unregistersCallbackOnDestruction) {
+ // TODO (b/279581095): With ftl::Function, `_` can be replaced with
+ // `mCallback`, here and in other calls to `registerCallback, since the
+ // ftl version has an operator==, unlike std::function.
+ EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+ .WillOnce(Return(mCallbackToken));
+ EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+
+ VSyncCallbackRegistration registration(mVsyncDispatch, mCallback, mCallbackName);
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration, mVsyncDispatch));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration, mCallbackToken));
+}
+
+TEST_F(VSyncCallbackRegistrationTest, unregistersCallbackOnPointerMove) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+ .WillOnce(Return(mCallbackToken));
+ EXPECT_CALL(*mVsyncDispatch2, registerCallback(_, mCallbackName2))
+ .WillOnce(Return(mCallbackToken2));
+ EXPECT_CALL(*mVsyncDispatch2, unregisterCallback(mCallbackToken2)).Times(1);
+ EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+ }
+
+ auto registration =
+ std::make_unique<VSyncCallbackRegistration>(mVsyncDispatch, mCallback, mCallbackName);
+
+ auto registration2 =
+ std::make_unique<VSyncCallbackRegistration>(mVsyncDispatch2, mCallback, mCallbackName2);
+
+ registration2 = std::move(registration);
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(*registration2.get(), mVsyncDispatch));
+ ASSERT_NO_FATAL_FAILURE(assertToken(*registration2.get(), mCallbackToken));
+}
+
+TEST_F(VSyncCallbackRegistrationTest, unregistersCallbackOnMoveOperator) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+ .WillOnce(Return(mCallbackToken));
+ EXPECT_CALL(*mVsyncDispatch2, registerCallback(_, mCallbackName2))
+ .WillOnce(Return(mCallbackToken2));
+ EXPECT_CALL(*mVsyncDispatch2, unregisterCallback(mCallbackToken2)).Times(1);
+ EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+ }
+
+ VSyncCallbackRegistration registration(mVsyncDispatch, mCallback, mCallbackName);
+
+ VSyncCallbackRegistration registration2(mVsyncDispatch2, mCallback, mCallbackName2);
+
+ registration2 = std::move(registration);
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration, nullptr));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration, std::nullopt));
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration2, mVsyncDispatch));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration2, mCallbackToken));
+}
+
+TEST_F(VSyncCallbackRegistrationTest, moveConstructor) {
+ EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+ .WillOnce(Return(mCallbackToken));
+ EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+
+ VSyncCallbackRegistration registration(mVsyncDispatch, mCallback, mCallbackName);
+ VSyncCallbackRegistration registration2(std::move(registration));
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration, nullptr));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration, std::nullopt));
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration2, mVsyncDispatch));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration2, mCallbackToken));
+}
+
+TEST_F(VSyncCallbackRegistrationTest, moveOperatorEqualsSelf) {
+ EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+ .WillOnce(Return(mCallbackToken));
+ EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+
+ VSyncCallbackRegistration registration(mVsyncDispatch, mCallback, mCallbackName);
+
+ // Use a reference so the compiler doesn't realize that registration is
+ // being moved to itself.
+ VSyncCallbackRegistration& registrationRef = registration;
+ registration = std::move(registrationRef);
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration, mVsyncDispatch));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration, mCallbackToken));
+}
+
+} // namespace android::scheduler