blob: dd7c7a9c37ad00484fbc1775669950bd42ad22c2 [file] [log] [blame]
/*
* 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.
*/
#include "os/reactor.h"
#include <sys/eventfd.h>
#include <chrono>
#include <future>
#include <thread>
#include "common/bind.h"
#include "common/callback.h"
#include "gtest/gtest.h"
#include "os/log.h"
namespace bluetooth {
namespace os {
namespace {
constexpr int kReadReadyValue = 100;
using common::Bind;
std::promise<int>* g_promise;
class ReactorTest : public ::testing::Test {
protected:
void SetUp() override {
g_promise = new std::promise<int>;
reactor_ = new Reactor;
}
void TearDown() override {
delete g_promise;
g_promise = nullptr;
delete reactor_;
reactor_ = nullptr;
}
Reactor* reactor_;
};
class SampleReactable {
public:
SampleReactable() : fd_(eventfd(0, EFD_NONBLOCK)) {
EXPECT_NE(fd_, -1);
}
~SampleReactable() {
close(fd_);
}
void OnReadReady() {}
void OnWriteReady() {}
int fd_;
};
class FakeReactable {
public:
enum EventFdValue {
kSetPromise = 1,
kRegisterSampleReactable,
kUnregisterSampleReactable,
kSampleOutputValue,
};
FakeReactable() : fd_(eventfd(0, 0)), reactor_(nullptr) {
EXPECT_NE(fd_, -1);
}
FakeReactable(Reactor* reactor) : fd_(eventfd(0, 0)), reactor_(reactor) {
EXPECT_NE(fd_, -1);
}
~FakeReactable() {
close(fd_);
}
void OnReadReady() {
LOG_INFO();
uint64_t value = 0;
auto read_result = eventfd_read(fd_, &value);
LOG_INFO("value = %d", (int)value);
EXPECT_EQ(read_result, 0);
if (value == kSetPromise && g_promise != nullptr) {
g_promise->set_value(kReadReadyValue);
}
if (value == kRegisterSampleReactable) {
reactable_ = reactor_->Register(
sample_reactable_.fd_,
Bind(&FakeReactable::OnReadReady, common::Unretained(this)),
Bind(&FakeReactable::OnWriteReadyNoOp, common::Unretained(this)));
g_promise->set_value(kReadReadyValue);
}
if (value == kUnregisterSampleReactable) {
reactor_->Unregister(reactable_);
g_promise->set_value(kReadReadyValue);
}
}
void OnWriteReady() {
auto write_result = eventfd_write(fd_, output_data_);
output_data_ = 0;
EXPECT_EQ(write_result, 0);
}
void OnWriteReadyNoOp() {}
void UnregisterInCallback() {
uint64_t value = 0;
auto read_result = eventfd_read(fd_, &value);
EXPECT_EQ(read_result, 0);
g_promise->set_value(kReadReadyValue);
reactor_->Unregister(reactable_);
}
SampleReactable sample_reactable_;
Reactor::Reactable* reactable_ = nullptr;
int fd_;
private:
Reactor* reactor_;
uint64_t output_data_ = kSampleOutputValue;
};
class FakeRunningReactable {
public:
FakeRunningReactable() : fd_(eventfd(0, 0)) {
EXPECT_NE(fd_, -1);
}
~FakeRunningReactable() {
close(fd_);
}
void OnReadReady() {
uint64_t value = 0;
auto read_result = eventfd_read(fd_, &value);
ASSERT_EQ(read_result, 0);
started.set_value();
can_finish.get_future().wait();
finished.set_value();
}
Reactor::Reactable* reactable_ = nullptr;
int fd_;
std::promise<void> started;
std::promise<void> can_finish;
std::promise<void> finished;
};
TEST_F(ReactorTest, start_and_stop) {
auto reactor_thread = std::thread(&Reactor::Run, reactor_);
reactor_->Stop();
reactor_thread.join();
}
TEST_F(ReactorTest, stop_and_start) {
auto reactor_thread = std::thread(&Reactor::Stop, reactor_);
auto another_thread = std::thread(&Reactor::Run, reactor_);
reactor_thread.join();
another_thread.join();
}
TEST_F(ReactorTest, stop_multi_times) {
auto reactor_thread = std::thread(&Reactor::Run, reactor_);
for (int i = 0; i < 5; i++) {
reactor_->Stop();
}
reactor_thread.join();
}
TEST_F(ReactorTest, cold_register_only) {
FakeReactable fake_reactable;
auto* reactable = reactor_->Register(
fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), common::Closure());
reactor_->Unregister(reactable);
}
TEST_F(ReactorTest, cold_register) {
FakeReactable fake_reactable;
auto* reactable = reactor_->Register(
fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), common::Closure());
auto reactor_thread = std::thread(&Reactor::Run, reactor_);
auto future = g_promise->get_future();
auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kSetPromise);
EXPECT_EQ(write_result, 0);
EXPECT_EQ(future.get(), kReadReadyValue);
reactor_->Stop();
reactor_thread.join();
reactor_->Unregister(reactable);
}
TEST_F(ReactorTest, hot_register_from_different_thread) {
auto reactor_thread = std::thread(&Reactor::Run, reactor_);
auto future = g_promise->get_future();
FakeReactable fake_reactable;
auto* reactable = reactor_->Register(
fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), common::Closure());
auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kSetPromise);
EXPECT_EQ(write_result, 0);
EXPECT_EQ(future.get(), kReadReadyValue);
reactor_->Stop();
reactor_thread.join();
reactor_->Unregister(reactable);
}
TEST_F(ReactorTest, unregister_from_different_thread_while_task_is_executing_) {
FakeRunningReactable fake_reactable;
auto* reactable = reactor_->Register(
fake_reactable.fd_,
Bind(&FakeRunningReactable::OnReadReady, common::Unretained(&fake_reactable)),
common::Closure());
auto reactor_thread = std::thread(&Reactor::Run, reactor_);
auto write_result = eventfd_write(fake_reactable.fd_, 1);
ASSERT_EQ(write_result, 0);
fake_reactable.started.get_future().wait();
reactor_->Unregister(reactable);
fake_reactable.can_finish.set_value();
fake_reactable.finished.get_future().wait();
reactor_->Stop();
reactor_thread.join();
}
TEST_F(ReactorTest, unregister_from_different_thread_while_task_is_executing_wait_fails) {
FakeRunningReactable fake_reactable;
auto* reactable = reactor_->Register(
fake_reactable.fd_,
common::Bind(&FakeRunningReactable::OnReadReady, common::Unretained(&fake_reactable)),
common::Closure());
auto reactor_thread = std::thread(&Reactor::Run, reactor_);
auto write_result = eventfd_write(fake_reactable.fd_, 1);
ASSERT_EQ(write_result, 0);
fake_reactable.started.get_future().wait();
reactor_->Unregister(reactable);
ASSERT_FALSE(reactor_->WaitForUnregisteredReactable(std::chrono::milliseconds(1)));
fake_reactable.can_finish.set_value();
fake_reactable.finished.get_future().wait();
reactor_->Stop();
reactor_thread.join();
}
TEST_F(ReactorTest, unregister_from_different_thread_while_task_is_executing_wait_succeeds) {
FakeRunningReactable fake_reactable;
auto* reactable = reactor_->Register(
fake_reactable.fd_,
common::Bind(&FakeRunningReactable::OnReadReady, common::Unretained(&fake_reactable)),
common::Closure());
auto reactor_thread = std::thread(&Reactor::Run, reactor_);
auto write_result = eventfd_write(fake_reactable.fd_, 1);
ASSERT_EQ(write_result, 0);
fake_reactable.started.get_future().wait();
reactor_->Unregister(reactable);
fake_reactable.can_finish.set_value();
fake_reactable.finished.get_future().wait();
ASSERT_TRUE(reactor_->WaitForUnregisteredReactable(std::chrono::milliseconds(1)));
reactor_->Stop();
reactor_thread.join();
}
TEST_F(ReactorTest, hot_unregister_from_different_thread) {
FakeReactable fake_reactable;
auto* reactable = reactor_->Register(
fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), common::Closure());
auto reactor_thread = std::thread(&Reactor::Run, reactor_);
reactor_->Unregister(reactable);
auto future = g_promise->get_future();
auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kSetPromise);
EXPECT_EQ(write_result, 0);
future.wait_for(std::chrono::milliseconds(10));
g_promise->set_value(2);
EXPECT_EQ(future.get(), 2);
reactor_->Stop();
reactor_thread.join();
}
TEST_F(ReactorTest, hot_register_from_same_thread) {
auto reactor_thread = std::thread(&Reactor::Run, reactor_);
auto future = g_promise->get_future();
FakeReactable fake_reactable(reactor_);
auto* reactable = reactor_->Register(
fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), common::Closure());
auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kRegisterSampleReactable);
EXPECT_EQ(write_result, 0);
EXPECT_EQ(future.get(), kReadReadyValue);
delete g_promise;
g_promise = new std::promise<int>;
future = g_promise->get_future();
write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kUnregisterSampleReactable);
EXPECT_EQ(write_result, 0);
reactor_->Stop();
reactor_thread.join();
reactor_->Unregister(reactable);
}
TEST_F(ReactorTest, hot_unregister_from_same_thread) {
auto reactor_thread = std::thread(&Reactor::Run, reactor_);
auto future = g_promise->get_future();
FakeReactable fake_reactable(reactor_);
auto* reactable = reactor_->Register(
fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), common::Closure());
auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kRegisterSampleReactable);
EXPECT_EQ(write_result, 0);
EXPECT_EQ(future.get(), kReadReadyValue);
LOG_INFO();
delete g_promise;
g_promise = new std::promise<int>;
future = g_promise->get_future();
write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kUnregisterSampleReactable);
EXPECT_EQ(write_result, 0);
EXPECT_EQ(future.get(), kReadReadyValue);
LOG_INFO();
reactor_->Stop();
reactor_thread.join();
reactor_->Unregister(reactable);
}
TEST_F(ReactorTest, hot_unregister_from_callback) {
auto reactor_thread = std::thread(&Reactor::Run, reactor_);
FakeReactable fake_reactable1(reactor_);
auto* reactable1 = reactor_->Register(
fake_reactable1.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable1)), common::Closure());
FakeReactable fake_reactable2(reactor_);
auto* reactable2 = reactor_->Register(
fake_reactable2.fd_,
Bind(&FakeReactable::UnregisterInCallback, common::Unretained(&fake_reactable2)),
common::Closure());
fake_reactable2.reactable_ = reactable2;
auto write_result = eventfd_write(fake_reactable2.fd_, 1);
EXPECT_EQ(write_result, 0);
reactor_->Stop();
reactor_thread.join();
reactor_->Unregister(reactable1);
}
TEST_F(ReactorTest, hot_unregister_during_unregister_from_callback) {
auto reactor_thread = std::thread(&Reactor::Run, reactor_);
auto future = g_promise->get_future();
FakeReactable fake_reactable1(reactor_);
auto* reactable1 = reactor_->Register(
fake_reactable1.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable1)), common::Closure());
FakeReactable fake_reactable2(reactor_);
auto* reactable2 = reactor_->Register(
fake_reactable2.fd_,
Bind(&FakeReactable::UnregisterInCallback, common::Unretained(&fake_reactable2)),
common::Closure());
fake_reactable2.reactable_ = reactable2;
auto write_result = eventfd_write(fake_reactable2.fd_, 1);
EXPECT_EQ(write_result, 0);
EXPECT_EQ(future.get(), kReadReadyValue);
reactor_->Unregister(reactable1);
reactor_->Stop();
reactor_thread.join();
}
TEST_F(ReactorTest, start_and_stop_multi_times) {
auto reactor_thread = std::thread(&Reactor::Run, reactor_);
reactor_->Stop();
reactor_thread.join();
for (int i = 0; i < 5; i++) {
reactor_thread = std::thread(&Reactor::Run, reactor_);
reactor_->Stop();
reactor_thread.join();
}
}
TEST_F(ReactorTest, on_write_ready) {
FakeReactable fake_reactable;
auto* reactable = reactor_->Register(
fake_reactable.fd_, common::Closure(), Bind(&FakeReactable::OnWriteReady, common::Unretained(&fake_reactable)));
auto reactor_thread = std::thread(&Reactor::Run, reactor_);
uint64_t value = 0;
auto read_result = eventfd_read(fake_reactable.fd_, &value);
EXPECT_EQ(read_result, 0);
EXPECT_EQ(value, FakeReactable::kSampleOutputValue);
reactor_->Stop();
reactor_thread.join();
reactor_->Unregister(reactable);
}
TEST_F(ReactorTest, modify_registration) {
FakeReactable fake_reactable;
auto* reactable = reactor_->Register(
fake_reactable.fd_, Bind(&FakeReactable::OnReadReady, common::Unretained(&fake_reactable)), common::Closure());
reactor_->ModifyRegistration(
reactable, common::Closure(), Bind(&FakeReactable::OnWriteReady, common::Unretained(&fake_reactable)));
auto reactor_thread = std::thread(&Reactor::Run, reactor_);
uint64_t value = 0;
auto read_result = eventfd_read(fake_reactable.fd_, &value);
EXPECT_EQ(read_result, 0);
EXPECT_EQ(value, FakeReactable::kSampleOutputValue);
reactor_->Stop();
reactor_thread.join();
reactor_->Unregister(reactable);
}
} // namespace
} // namespace os
} // namespace bluetooth