/*
* 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