| /* |
| * Copyright 2018 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 LOG_TAG "BufferHubEventFdTest" |
| |
| #include <sys/epoll.h> |
| #include <sys/eventfd.h> |
| |
| #include <array> |
| #include <condition_variable> |
| #include <mutex> |
| #include <thread> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <log/log.h> |
| #include <ui/BufferHubEventFd.h> |
| |
| namespace android { |
| |
| namespace { |
| |
| const int kTimeout = 100; |
| const std::chrono::milliseconds kTimeoutMs(kTimeout); |
| const int kTestRuns = 5; |
| |
| using ::testing::Contains; |
| using BufferHubEventFdTest = ::testing::Test; |
| |
| } // namespace |
| |
| TEST_F(BufferHubEventFdTest, EventFd_testSingleEpollFd) { |
| BufferHubEventFd eventFd; |
| ASSERT_TRUE(eventFd.isValid()); |
| |
| base::unique_fd epollFd(epoll_create(64)); |
| ASSERT_GE(epollFd.get(), 0); |
| |
| epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; |
| ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); |
| |
| std::array<epoll_event, 1> events; |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); |
| |
| eventFd.signal(); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); |
| |
| // The epoll fd is edge triggered, so it only responds to the eventFd once. |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); |
| |
| // Check that it can receive consecutive signal. |
| eventFd.signal(); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); |
| |
| // Check that it can receive consecutive signal from a duplicated eventfd. |
| BufferHubEventFd dupEventFd(dup(eventFd.get())); |
| ASSERT_TRUE(dupEventFd.isValid()); |
| dupEventFd.signal(); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); |
| dupEventFd.signal(); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); |
| } |
| |
| TEST_F(BufferHubEventFdTest, EventFd_testCreateEpollFdAndAddSignaledEventFd) { |
| BufferHubEventFd eventFd; |
| ASSERT_TRUE(eventFd.isValid()); |
| eventFd.signal(); |
| |
| base::unique_fd epollFd(epoll_create(64)); |
| ASSERT_GE(epollFd.get(), 0); |
| |
| // Make sure that the epoll set has not been signal yet. |
| std::array<epoll_event, 1> events; |
| ASSERT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); |
| |
| // Check that adding an signaled fd into this epoll set will trigger the epoll set. |
| epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; |
| ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); |
| |
| // The epoll fd is edge triggered, so it only responds to the eventFd once. |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); |
| } |
| |
| TEST_F(BufferHubEventFdTest, EventFd_testAddSignaledEventFdToEpollFd) { |
| BufferHubEventFd eventFd; |
| ASSERT_TRUE(eventFd.isValid()); |
| |
| base::unique_fd epollFd(epoll_create(64)); |
| ASSERT_GE(epollFd.get(), 0); |
| |
| eventFd.signal(); |
| |
| epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; |
| ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); |
| |
| std::array<epoll_event, 1> events; |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); |
| |
| // The epoll fd is edge triggered, so it only responds to the eventFd once. |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); |
| } |
| |
| TEST_F(BufferHubEventFdTest, EventFd_testConsecutiveSignalsFromAEventFd) { |
| BufferHubEventFd eventFd; |
| ASSERT_TRUE(eventFd.isValid()); |
| base::unique_fd epollFd(epoll_create(64)); |
| ASSERT_GE(epollFd.get(), 0); |
| epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; |
| ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); |
| |
| std::array<epoll_event, 1> events; |
| for (int i = 0; i < kTestRuns; ++i) { |
| eventFd.signal(); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); |
| } |
| } |
| |
| TEST_F(BufferHubEventFdTest, EventFd_testConsecutiveSignalsFromADuplicatedEventFd) { |
| BufferHubEventFd eventFd; |
| ASSERT_TRUE(eventFd.isValid()); |
| base::unique_fd epollFd(epoll_create(64)); |
| ASSERT_GE(epollFd.get(), 0); |
| epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; |
| ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); |
| |
| BufferHubEventFd dupEventFd(dup(eventFd.get())); |
| ASSERT_TRUE(dupEventFd.isValid()); |
| |
| std::array<epoll_event, 1> events; |
| for (int i = 0; i < kTestRuns; ++i) { |
| dupEventFd.signal(); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); |
| } |
| } |
| |
| TEST_F(BufferHubEventFdTest, EventFd_testClear) { |
| BufferHubEventFd eventFd; |
| ASSERT_TRUE(eventFd.isValid()); |
| |
| base::unique_fd epollFd(epoll_create(64)); |
| epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; |
| |
| ASSERT_GE(epollFd.get(), 0); |
| ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); |
| |
| eventFd.signal(); |
| eventFd.clear(); |
| |
| std::array<epoll_event, 1> events; |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); |
| } |
| |
| TEST_F(BufferHubEventFdTest, EventFd_testDupEventFd) { |
| BufferHubEventFd eventFd; |
| ASSERT_TRUE(eventFd.isValid()); |
| |
| base::unique_fd epollFd(epoll_create(64)); |
| epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; |
| |
| ASSERT_GE(epollFd.get(), 0); |
| ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); |
| |
| // Technically, the dupliated eventFd and the original eventFd are pointing |
| // to the same kernel object. This test signals the duplicated eventFd but epolls the origianl |
| // eventFd. |
| BufferHubEventFd dupedEventFd(dup(eventFd.get())); |
| ASSERT_GE(dupedEventFd.get(), 0); |
| |
| std::array<epoll_event, 1> events; |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); |
| |
| dupedEventFd.signal(); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); |
| |
| // The epoll fd is edge triggered, so it only responds to the eventFd once. |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); |
| |
| dupedEventFd.signal(); |
| |
| dupedEventFd.clear(); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); |
| } |
| |
| TEST_F(BufferHubEventFdTest, EventFd_testTwoEpollFds) { |
| BufferHubEventFd eventFd; |
| ASSERT_TRUE(eventFd.isValid()); |
| |
| base::unique_fd epollFd1(epoll_create(64)); |
| base::unique_fd epollFd2(epoll_create(64)); |
| epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; |
| |
| ASSERT_GE(epollFd1.get(), 0); |
| ASSERT_GE(epollFd2.get(), 0); |
| |
| // Register the same eventFd to two EpollFds. |
| ASSERT_EQ(epoll_ctl(epollFd1.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); |
| ASSERT_EQ(epoll_ctl(epollFd2.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); |
| |
| std::array<epoll_event, 1> events; |
| EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0); |
| EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0); |
| |
| eventFd.signal(); |
| EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 1); |
| EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 1); |
| |
| // The epoll fd is edge triggered, so it only responds to the eventFd once. |
| EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0); |
| EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0); |
| |
| eventFd.signal(); |
| EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 1); |
| |
| eventFd.clear(); |
| EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0); |
| EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0); |
| } |
| |
| TEST_F(BufferHubEventFdTest, EventFd_testTwoEventFds) { |
| BufferHubEventFd eventFd1; |
| BufferHubEventFd eventFd2; |
| |
| ASSERT_TRUE(eventFd1.isValid()); |
| ASSERT_TRUE(eventFd2.isValid()); |
| |
| base::unique_fd epollFd(epoll_create(64)); |
| epoll_event e1 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 1}}; |
| epoll_event e2 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 2}}; |
| |
| ASSERT_GE(epollFd.get(), 0); |
| ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e1), 0); |
| ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd2.get(), &e2), 0); |
| |
| std::array<epoll_event, 2> events; |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); |
| |
| // Signal one by one. |
| eventFd1.signal(); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); |
| EXPECT_EQ(events[0].data.u32, e1.data.u32); |
| |
| eventFd2.signal(); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); |
| EXPECT_EQ(events[0].data.u32, e2.data.u32); |
| |
| // Signal both. |
| eventFd1.signal(); |
| eventFd2.signal(); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 2); |
| |
| uint32_t u32s[] = {events[0].data.u32, events[1].data.u32}; |
| EXPECT_THAT(u32s, Contains(e1.data.u32)); |
| EXPECT_THAT(u32s, Contains(e2.data.u32)); |
| |
| // The epoll fd is edge triggered, so it only responds to the eventFd once. |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); |
| |
| eventFd1.signal(); |
| eventFd2.signal(); |
| eventFd2.clear(); |
| EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); |
| } |
| |
| TEST_F(BufferHubEventFdTest, EventFd_testPollingThreadWithTwoEventFds) { |
| BufferHubEventFd eventFd1; |
| BufferHubEventFd eventFd2; |
| |
| ASSERT_TRUE(eventFd1.isValid()); |
| ASSERT_TRUE(eventFd2.isValid()); |
| |
| base::unique_fd epollFd(epoll_create(64)); |
| epoll_event e1 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 1}}; |
| epoll_event e2 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 2}}; |
| |
| ASSERT_GE(epollFd.get(), 0); |
| ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e1), 0); |
| ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd2.get(), &e2), 0); |
| |
| int countEvent1 = 0; |
| int countEvent2 = 0; |
| std::atomic<bool> stop{false}; |
| std::mutex mx; |
| std::condition_variable cv; |
| |
| std::thread pollingThread([&] { |
| std::array<epoll_event, 2> events; |
| while (true) { |
| if (stop.load()) { |
| break; |
| } |
| int ret = epoll_wait(epollFd.get(), events.data(), events.size(), kTimeout); |
| ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed."); |
| |
| std::lock_guard<std::mutex> lock(mx); |
| for (int i = 0; i < ret; i++) { |
| if (events[i].data.u32 == e1.data.u32) { |
| countEvent1++; |
| cv.notify_one(); |
| } else if (events[i].data.u32 == e2.data.u32) { |
| countEvent2++; |
| cv.notify_one(); |
| } |
| } |
| } |
| }); |
| |
| { |
| std::unique_lock<std::mutex> lock(mx); |
| |
| eventFd1.signal(); |
| EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 1; })); |
| |
| eventFd1.signal(); |
| EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 2; })); |
| |
| eventFd2.signal(); |
| EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent2 == 1; })); |
| |
| eventFd1.clear(); |
| eventFd2.clear(); |
| EXPECT_EQ(countEvent1, 2); |
| EXPECT_EQ(countEvent2, 1); |
| |
| eventFd1.signal(); |
| EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 3; })); |
| |
| eventFd2.signal(); |
| EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent2 == 2; })); |
| } |
| |
| stop.store(true); |
| pollingThread.join(); |
| } |
| |
| TEST_F(BufferHubEventFdTest, EventFd_testTwoPollingThreads) { |
| BufferHubEventFd eventFd; |
| ASSERT_TRUE(eventFd.isValid()); |
| |
| base::unique_fd epollFd1(epoll_create(64)); |
| base::unique_fd epollFd2(epoll_create(64)); |
| epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; |
| |
| ASSERT_GE(epollFd1.get(), 0); |
| ASSERT_GE(epollFd2.get(), 0); |
| |
| // Register the same eventFd to two EpollFds. |
| ASSERT_EQ(epoll_ctl(epollFd1.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); |
| ASSERT_EQ(epoll_ctl(epollFd2.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); |
| |
| int countEpoll1 = 0; |
| int countEpoll2 = 0; |
| std::atomic<bool> stop{false}; |
| std::mutex mx; |
| std::condition_variable cv; |
| |
| std::thread pollingThread1([&] { |
| std::array<epoll_event, 1> events; |
| while (!stop.load()) { |
| int ret = epoll_wait(epollFd1.get(), events.data(), events.size(), kTimeout); |
| ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed."); |
| |
| if (ret > 0) { |
| std::lock_guard<std::mutex> lock(mx); |
| countEpoll1++; |
| cv.notify_one(); |
| } |
| } |
| }); |
| |
| std::thread pollingThread2([&] { |
| std::array<epoll_event, 1> events; |
| while (!stop.load()) { |
| int ret = epoll_wait(epollFd2.get(), events.data(), events.size(), kTimeout); |
| ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed."); |
| |
| if (ret > 0) { |
| std::lock_guard<std::mutex> lock(mx); |
| countEpoll2++; |
| cv.notify_one(); |
| } |
| } |
| }); |
| |
| { |
| std::unique_lock<std::mutex> lock(mx); |
| |
| eventFd.signal(); |
| EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 1; })); |
| EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 1; })); |
| |
| eventFd.signal(); |
| EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 2; })); |
| EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 2; })); |
| |
| eventFd.clear(); |
| EXPECT_EQ(countEpoll1, 2); |
| EXPECT_EQ(countEpoll2, 2); |
| |
| eventFd.signal(); |
| EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 3; })); |
| EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 3; })); |
| } |
| |
| stop.store(true); |
| pollingThread1.join(); |
| pollingThread2.join(); |
| } |
| |
| } // namespace android |