/* * 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 #include #include #include #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* g_promise; class ReactorTest : public ::testing::Test { protected: void SetUp() override { g_promise = new std::promise; 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 started; std::promise can_finish; std::promise 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; 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; 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