},
{
"name" : "net_test_types"
- },
+ }
+ ],
+ "presubmit" : [
{
"name" : "bluetooth_test_common",
"host" : true
],
srcs: [
"address_obfuscator.cc",
- "alarm.cc",
- "handler.cc",
"message_loop_thread.cc",
"metrics.cc",
"once_timer.cc",
- "reactor.cc",
- "repeating_alarm.cc",
"repeating_timer.cc",
- "thread.cc",
"time_util.cc",
],
shared_libs: [
],
srcs : [
"address_obfuscator_unittest.cc",
- "alarm_unittest.cc",
- "handler_unittest.cc",
"leaky_bonded_queue_unittest.cc",
"message_loop_thread_unittest.cc",
"metrics_unittest.cc",
"once_timer_unittest.cc",
- "reactor_unittest.cc",
- "repeating_alarm_unittest.cc",
"repeating_timer_unittest.cc",
"state_machine_unittest.cc",
- "thread_unittest.cc",
"time_util_unittest.cc",
"id_generator_unittest.cc",
],
+++ /dev/null
-/*
- * 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 "alarm.h"
-
-#include <sys/timerfd.h>
-#include <cstring>
-
-#include "base/logging.h"
-#include "utils.h"
-
-namespace bluetooth {
-namespace common {
-
-Alarm::Alarm(Thread* thread)
- : thread_(thread),
- fd_(timerfd_create(CLOCK_BOOTTIME_ALARM, 0)) {
- CHECK_NE(fd_, -1) << __func__ << ": cannot create timerfd: " << strerror(errno);
-
- token_ = thread_->GetReactor()->Register(fd_, [this] { on_fire(); }, nullptr);
-}
-
-Alarm::~Alarm() {
- thread_->GetReactor()->Unregister(token_);
-
- int close_status;
- RUN_NO_INTR(close_status = close(fd_));
- CHECK_NE(close_status, -1) << __func__ << ": cannot close timerfd: " << strerror(errno);
-}
-
-void Alarm::Schedule(Closure task, std::chrono::milliseconds delay) {
- std::lock_guard<std::mutex> lock(mutex_);
- long delay_ms = delay.count();
- itimerspec timer_itimerspec{
- {/* interval for periodic timer */},
- {delay_ms / 1000, delay_ms % 1000 * 1000000}
- };
- int result = timerfd_settime(fd_, 0, &timer_itimerspec, nullptr);
- CHECK_EQ(result, 0) << __func__ << ": failed, error=" << strerror(errno);
-
- task_ = std::move(task);
-}
-
-void Alarm::Cancel() {
- std::lock_guard<std::mutex> lock(mutex_);
- itimerspec disarm_itimerspec{/* disarm timer */};
- int result = timerfd_settime(fd_, 0, &disarm_itimerspec, nullptr);
- CHECK_EQ(result, 0) << __func__ << ": failed, error=" << strerror(errno);
-}
-
-void Alarm::on_fire() {
- std::unique_lock<std::mutex> lock(mutex_);
- auto task = std::move(task_);
- uint64_t times_invoked;
- auto bytes_read = read(fd_, ×_invoked, sizeof(uint64_t));
- lock.unlock();
- task();
- CHECK_EQ(bytes_read, static_cast<ssize_t>(sizeof(uint64_t))) << __func__ << ": failed, error=" << strerror(errno);
- CHECK_EQ(times_invoked, static_cast<uint64_t>(1));
-}
-
-} // namespace common
-} // namespace bluetooth
+++ /dev/null
-/*
- * 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.
- */
-
-#pragma once
-
-#include <functional>
-#include <memory>
-#include <mutex>
-
-#include "common/thread.h"
-#include "common/utils.h"
-
-namespace bluetooth {
-namespace common {
-
-// A single-shot alarm for reactor-based thread, implemented by Linux timerfd.
-// When it's constructed, it will register a reactable on the specified thread; when it's destroyed, it will unregister
-// itself from the thread.
-class Alarm {
- public:
- // Create and register a single-shot alarm on given thread
- explicit Alarm(Thread* thread);
-
- // Unregister this alarm from the thread and release resource
- ~Alarm();
-
- DISALLOW_COPY_AND_ASSIGN(Alarm);
-
- // Schedule the alarm with given delay
- void Schedule(Closure task, std::chrono::milliseconds delay);
-
- // Cancel the alarm. No-op if it's not armed.
- void Cancel();
-
- private:
- Closure task_;
- Thread* thread_;
- int fd_ = 0;
- Reactor::Reactable* token_;
- mutable std::mutex mutex_;
- void on_fire();
-};
-
-} // namespace common
-
-} // namespace bluetooth
+++ /dev/null
-/*
- * 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 "alarm.h"
-
-#include <future>
-
-#include "base/logging.h"
-#include "gtest/gtest.h"
-
-namespace bluetooth {
-namespace common {
-namespace {
-
-class AlarmTest : public ::testing::Test {
- protected:
- void SetUp() override {
- thread_ = new Thread("test_thread", Thread::Priority::NORMAL);
- alarm_ = new Alarm(thread_);
- }
-
- void TearDown() override {
- delete alarm_;
- delete thread_;
- }
- Alarm* alarm_;
-
- private:
- Thread* thread_;
-};
-
-TEST_F(AlarmTest, cancel_while_not_armed) {
- alarm_->Cancel();
-}
-
-TEST_F(AlarmTest, schedule) {
- std::promise<void> promise;
- auto future = promise.get_future();
- auto before = std::chrono::steady_clock::now();
- int delay_ms = 10;
- int delay_error_ms = 3;
- alarm_->Schedule([&promise]() { promise.set_value(); }, std::chrono::milliseconds(delay_ms));
- future.get();
- auto after = std::chrono::steady_clock::now();
- auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(after - before);
- ASSERT_NEAR(duration_ms.count(), delay_ms, delay_error_ms);
-}
-
-TEST_F(AlarmTest, cancel_alarm) {
- alarm_->Schedule([]() { ASSERT_TRUE(false) << "Should not happen"; }, std::chrono::milliseconds(3));
- alarm_->Cancel();
- std::this_thread::sleep_for(std::chrono::milliseconds(5));
-}
-
-TEST_F(AlarmTest, cancel_alarm_from_callback) {
- alarm_->Schedule([this]() { this->alarm_->Cancel(); }, std::chrono::milliseconds(1));
- std::this_thread::sleep_for(std::chrono::milliseconds(5));
-}
-
-TEST_F(AlarmTest, schedule_while_alarm_armed) {
- alarm_->Schedule([]() { ASSERT_TRUE(false) << "Should not happen"; }, std::chrono::milliseconds(1));
- std::promise<void> promise;
- auto future = promise.get_future();
- alarm_->Schedule([&promise]() { promise.set_value(); }, std::chrono::milliseconds(10));
- future.get();
-}
-
-TEST_F(AlarmTest, delete_while_alarm_armed) {
- alarm_->Schedule([]() { ASSERT_TRUE(false) << "Should not happen"; }, std::chrono::milliseconds(1));
- delete alarm_;
- alarm_ = nullptr;
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
-}
-
-} // namespace
-} // namespace common
-} // namespace bluetooth
#include <memory>
#include <thread>
-#include "common/handler.h"
#include "common/message_loop_thread.h"
-#include "common/thread.h"
#include "osi/include/fixed_queue.h"
#include "osi/include/thread.h"
using ::benchmark::State;
-using bluetooth::common::Handler;
using bluetooth::common::MessageLoopThread;
-using bluetooth::common::Thread;
#define NUM_MESSAGES_TO_SEND 100000
}
};
-class BM_ReactorThread : public BM_ThreadPerformance {
- protected:
- void SetUp(State& st) override {
- BM_ThreadPerformance::SetUp(st);
- std::future<void> set_up_future = set_up_promise_->get_future();
- thread_ = new Thread("BM_ReactorThread thread", Thread::Priority::NORMAL);
- handler_ = new Handler(thread_);
- handler_->Post([this]() { set_up_promise_->set_value(); });
- set_up_future.wait();
- }
-
- void TearDown(State& st) override {
- delete handler_;
- handler_ = nullptr;
- thread_->Stop();
- delete thread_;
- thread_ = nullptr;
- BM_ThreadPerformance::TearDown(st);
- }
-
- Thread* thread_ = nullptr;
- Handler* handler_ = nullptr;
-};
-
-BENCHMARK_F(BM_ReactorThread, batch_enque_dequeue)(State& state) {
- for (auto _ : state) {
- g_counter = 0;
- g_counter_promise = std::make_unique<std::promise<void>>();
- std::future<void> counter_future = g_counter_promise->get_future();
- for (int i = 0; i < NUM_MESSAGES_TO_SEND; i++) {
- fixed_queue_enqueue(bt_msg_queue_, (void*)&g_counter);
- handler_->Post([this]() { callback_batch(bt_msg_queue_, nullptr); });
- }
- counter_future.wait();
- }
-};
-
-BENCHMARK_F(BM_ReactorThread, sequential_execution)(State& state) {
- for (auto _ : state) {
- for (int i = 0; i < NUM_MESSAGES_TO_SEND; i++) {
- g_counter_promise = std::make_unique<std::promise<void>>();
- std::future<void> counter_future = g_counter_promise->get_future();
- handler_->Post([]() { callback_sequential(nullptr); });
- counter_future.wait();
- }
- }
-};
-
int main(int argc, char** argv) {
// Disable LOG() output from libchrome
logging::LoggingSettings log_settings;
#include <benchmark/benchmark.h>
#include <future>
-#include "common/alarm.h"
#include "common/message_loop_thread.h"
#include "common/once_timer.h"
-#include "common/repeating_alarm.h"
#include "common/repeating_timer.h"
-#include "common/thread.h"
#include "common/time_util.h"
#include "osi/include/alarm.h"
using ::benchmark::State;
-using bluetooth::common::Alarm;
using bluetooth::common::MessageLoopThread;
using bluetooth::common::OnceTimer;
-using bluetooth::common::RepeatingAlarm;
using bluetooth::common::RepeatingTimer;
-using bluetooth::common::Thread;
using bluetooth::common::time_get_os_boottime_us;
// fake get_main_message_loop implementation for alarm
->Iterations(1)
->UseRealTime();
-class BM_ReactableAlarm : public ::benchmark::Fixture {
- protected:
- void SetUp(State& st) override {
- ::benchmark::Fixture::SetUp(st);
- thread_ = std::make_unique<Thread>("timer_benchmark", Thread::Priority::REAL_TIME);
- alarm_ = std::make_unique<Alarm>(thread_.get());
- repeating_alarm_ = std::make_unique<RepeatingAlarm>(thread_.get());
- g_map.clear();
- g_promise = std::make_shared<std::promise<void>>();
- g_scheduled_tasks = 0;
- g_task_length = 0;
- g_task_interval = 0;
- g_task_counter = 0;
- }
-
- void TearDown(State& st) override {
- g_promise = nullptr;
- alarm_ = nullptr;
- repeating_alarm_ = nullptr;
- thread_->Stop();
- thread_ = nullptr;
- ::benchmark::Fixture::TearDown(st);
- }
-
- std::unique_ptr<Thread> thread_;
- std::unique_ptr<Alarm> alarm_;
- std::unique_ptr<RepeatingAlarm> repeating_alarm_;
-};
-
-BENCHMARK_DEFINE_F(BM_ReactableAlarm, timer_performance_ms)(State& state) {
- auto milliseconds = static_cast<int>(state.range(0));
- for (auto _ : state) {
- auto start_time_point = time_get_os_boottime_us();
- alarm_->Schedule(std::bind(TimerFire, nullptr), std::chrono::milliseconds(milliseconds));
- g_promise->get_future().get();
- auto end_time_point = time_get_os_boottime_us();
- auto duration = end_time_point - start_time_point;
- state.SetIterationTime(duration * 1e-6);
- alarm_->Cancel();
- }
-};
-
-BENCHMARK_REGISTER_F(BM_ReactableAlarm, timer_performance_ms)
- ->Arg(1)
- ->Arg(5)
- ->Arg(10)
- ->Arg(20)
- ->Arg(100)
- ->Arg(1000)
- ->Arg(2000)
- ->Iterations(1)
- ->UseRealTime();
-
-BENCHMARK_DEFINE_F(BM_ReactableAlarm, periodic_accuracy)
-(State& state) {
- for (auto _ : state) {
- g_scheduled_tasks = state.range(0);
- g_task_length = state.range(1);
- g_task_interval = state.range(2);
- g_start_time = time_get_os_boottime_us();
- repeating_alarm_->Schedule([] { AlarmSleepAndCountDelayedTime(nullptr); },
- std::chrono::milliseconds(g_task_interval));
- g_promise->get_future().get();
- repeating_alarm_->Cancel();
- }
- for (const auto& delay : g_map) {
- state.counters[std::to_string(delay.first)] = delay.second;
- }
-};
-
-BENCHMARK_REGISTER_F(BM_ReactableAlarm, periodic_accuracy)
- ->Args({2000, 1, 5})
- ->Args({2000, 3, 5})
- ->Args({2000, 1, 7})
- ->Args({2000, 3, 7})
- ->Args({2000, 1, 20})
- ->Args({2000, 5, 20})
- ->Args({2000, 10, 20})
- ->Args({2000, 15, 20})
- ->Iterations(1)
- ->UseRealTime();
-
int main(int argc, char** argv) {
// Disable LOG() output from libchrome
logging::LoggingSettings log_settings;
+++ /dev/null
-/*
- * 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 "handler.h"
-
-#include <sys/eventfd.h>
-#include <cstring>
-
-#include "base/logging.h"
-
-#include "reactor.h"
-#include "utils.h"
-
-#ifndef EFD_SEMAPHORE
-#define EFD_SEMAPHORE 1
-#endif
-
-namespace bluetooth {
-namespace common {
-
-Handler::Handler(Thread* thread)
- : thread_(thread),
- fd_(eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK)) {
- CHECK_NE(fd_, -1) << __func__ << ": cannot create eventfd: " << strerror(errno);
-
- reactable_ = thread_->GetReactor()->Register(fd_, [this] { this->handle_next_event(); }, nullptr);
-}
-
-Handler::~Handler() {
- thread_->GetReactor()->Unregister(reactable_);
- reactable_ = nullptr;
-
- int close_status;
- RUN_NO_INTR(close_status = close(fd_));
- CHECK_NE(close_status, -1) << __func__ << ": cannot close eventfd: " << strerror(errno);
-}
-
-void Handler::Post(Closure closure) {
- {
- std::lock_guard<std::mutex> lock(mutex_);
- tasks_.emplace(std::move(closure));
- }
- uint64_t val = 1;
- auto write_result = eventfd_write(fd_, val);
- CHECK_NE(write_result, -1) << __func__ << ": failed to write: " << strerror(errno);
-}
-
-void Handler::Clear() {
- std::lock_guard<std::mutex> lock(mutex_);
-
- std::queue<Closure> empty;
- std::swap(tasks_, empty);
-
- uint64_t val;
- while (eventfd_read(fd_, &val) == 0) {
- }
-}
-
-void Handler::handle_next_event() {
- Closure closure;
- uint64_t val = 0;
- auto read_result = eventfd_read(fd_, &val);
- CHECK_NE(read_result, -1) << __func__ << ": failed to read fd: " << strerror(errno);
-
- {
- std::lock_guard<std::mutex> lock(mutex_);
- closure = std::move(tasks_.front());
- tasks_.pop();
- }
- closure();
-}
-
-} // namespace common
-} // namespace bluetooth
+++ /dev/null
-/*
- * 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.
- */
-
-#pragma once
-
-#include <functional>
-#include <memory>
-#include <mutex>
-#include <queue>
-
-#include "common/thread.h"
-#include "common/utils.h"
-
-namespace bluetooth {
-namespace common {
-
-// A message-queue style handler for reactor-based thread to handle incoming events from different threads. When it's
-// constructed, it will register a reactable on the specified thread; when it's destroyed, it will unregister itself
-// from the thread.
-class Handler {
- public:
- // Create and register a handler on given thread
- explicit Handler(Thread* thread);
-
- // Unregister this handler from the thread and release resource. Unhandled events will be discarded and not executed.
- ~Handler();
-
- DISALLOW_COPY_AND_ASSIGN(Handler);
-
- // Enqueue a closure to the queue of this handler
- void Post(Closure closure);
-
- // Remove all pending events from the queue of this handler
- void Clear();
-
- private:
- std::queue<Closure> tasks_;
- Thread* thread_;
- int fd_;
- Reactor::Reactable* reactable_;
- mutable std::mutex mutex_;
- void handle_next_event();
-};
-
-} // namespace common
-} // namespace bluetooth
+++ /dev/null
-/*
- * 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 "handler.h"
-
-#include <sys/eventfd.h>
-#include <thread>
-
-#include <gtest/gtest.h>
-#include "base/logging.h"
-
-namespace bluetooth {
-namespace common {
-namespace {
-
-class HandlerTest : public ::testing::Test {
- protected:
- void SetUp() override {
- thread_ = new Thread("test_thread", Thread::Priority::NORMAL);
- handler_ = new Handler(thread_);
- }
- void TearDown() override {
- delete handler_;
- delete thread_;
- }
-
- Handler* handler_;
- Thread* thread_;
-};
-
-TEST_F(HandlerTest, empty) {}
-
-TEST_F(HandlerTest, post_task_invoked) {
- int val = 0;
- Closure closure = [&val]() { val++; };
- handler_->Post(closure);
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
- EXPECT_EQ(val, 1);
-}
-
-TEST_F(HandlerTest, post_task_cleared) {
- int val = 0;
- Closure closure = [&val]() {
- val++;
- std::this_thread::sleep_for(std::chrono::milliseconds(5));
- };
- handler_->Post(std::move(closure));
- closure = []() { EXPECT_EQ(0, 1) << "Should not happen"; };
- std::this_thread::sleep_for(std::chrono::milliseconds(5));
- handler_->Post(std::move(closure));
- handler_->Clear();
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
- EXPECT_EQ(val, 1);
-}
-
-TEST_F(HandlerTest, post_task_from_callback) {
- int val = 0;
- Closure closure = [&val, this] {
- Closure inner_closure = [&val] { val++; };
- handler_->Post(inner_closure);
- };
- handler_->Post(closure);
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
- EXPECT_EQ(val, 1);
-}
-
-TEST_F(HandlerTest, clear_task_from_callback) {
- int val = 0;
- Closure closure = [&val, this] {
- val++;
- handler_->Clear();
- std::this_thread::sleep_for(std::chrono::milliseconds(5));
- };
- handler_->Post(std::move(closure));
- closure = []() { EXPECT_EQ(0, 1) << "Should not happen"; };
- handler_->Post(std::move(closure));
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
- EXPECT_EQ(val, 1);
-}
-
-} // namespace
-} // namespace common
-} // namespace bluetooth
+++ /dev/null
-/*
- * 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 "reactor.h"
-
-#include <fcntl.h>
-#include <sys/epoll.h>
-#include <sys/eventfd.h>
-#include <unistd.h>
-#include <algorithm>
-#include <cerrno>
-#include <cstring>
-
-#include "base/logging.h"
-
-namespace {
-
-// Use at most sizeof(epoll_event) * kEpollMaxEvents kernel memory
-constexpr int kEpollMaxEvents = 64;
-
-} // namespace
-
-namespace bluetooth {
-namespace common {
-
-class Reactor::Reactable {
- public:
- Reactable(int fd, Closure on_read_ready, Closure on_write_ready)
- : fd_(fd),
- on_read_ready_(std::move(on_read_ready)),
- on_write_ready_(std::move(on_write_ready)),
- is_executing_(false) {}
- const int fd_;
- Closure on_read_ready_;
- Closure on_write_ready_;
- bool is_executing_;
- std::recursive_mutex lock_;
-};
-
-Reactor::Reactor()
- : epoll_fd_(0),
- control_fd_(0),
- is_running_(false),
- reactable_removed_(false) {
- RUN_NO_INTR(epoll_fd_ = epoll_create1(EPOLL_CLOEXEC));
- CHECK_NE(epoll_fd_, -1) << __func__ << ": cannot create epoll_fd: " << strerror(errno);
-
- control_fd_ = eventfd(0, EFD_NONBLOCK);
- CHECK_NE(control_fd_, -1) << __func__ << ": cannot create control_fd: " << strerror(errno);
-
- epoll_event control_epoll_event = {EPOLLIN, {.ptr = nullptr}};
- int result;
- RUN_NO_INTR(result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, control_fd_, &control_epoll_event));
- CHECK_NE(result, -1) << __func__ << ": cannot register control_fd: " << strerror(errno);
-}
-
-Reactor::~Reactor() {
- int result;
- RUN_NO_INTR(result = epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, control_fd_, nullptr));
- CHECK_NE(result, -1) << __func__ << ": cannot unregister control_fd: " << strerror(errno);
-
- RUN_NO_INTR(result = close(control_fd_));
- CHECK_NE(result, -1) << __func__ << ": cannot close control_fd: " << strerror(errno);
-
- RUN_NO_INTR(result = close(epoll_fd_));
- CHECK_NE(result, -1) << __func__ << ": cannot close epoll_fd: " << strerror(errno);
-}
-
-void Reactor::Run() {
- bool previously_running = is_running_.exchange(true);
- CHECK_EQ(previously_running, false) << __func__ << ": already running";
- LOG(INFO) << __func__ << ": started";
-
- for (;;) {
- invalidation_list_.clear();
- epoll_event events[kEpollMaxEvents];
- int count;
- RUN_NO_INTR(count = epoll_wait(epoll_fd_, events, kEpollMaxEvents, -1));
- CHECK_NE(count, -1) << __func__ << ": Error polling for fds: " << strerror(errno);
-
- for (int i = 0; i < count; ++i) {
- auto event = events[i];
- CHECK_NE(event.events, 0u) << __func__ << ": no result in epoll result";
-
- // If the ptr stored in epoll_event.data is nullptr, it means the control fd triggered
- if (event.data.ptr == nullptr) {
- uint64_t value;
- eventfd_read(control_fd_, &value);
- LOG(INFO) << __func__ << ": stopped";
- is_running_ = false;
- return;
- }
- auto* reactable = static_cast<Reactor::Reactable*>(event.data.ptr);
- {
- std::unique_lock<std::mutex> lock(mutex_);
- // See if this reactable has been removed in the meantime.
- if (std::find(invalidation_list_.begin(), invalidation_list_.end(), reactable) != invalidation_list_.end()) {
- continue;
- }
-
- std::lock_guard<std::recursive_mutex> reactable_lock(reactable->lock_);
- lock.unlock();
- reactable_removed_ = false;
- reactable->is_executing_ = true;
- if (event.events & (EPOLLIN | EPOLLHUP | EPOLLRDHUP | EPOLLERR) && reactable->on_read_ready_ != nullptr) {
- reactable->on_read_ready_();
- }
- if (!reactable_removed_ && event.events & EPOLLOUT && reactable->on_write_ready_ != nullptr) {
- reactable->on_write_ready_();
- }
- reactable->is_executing_ = false;
- }
- if (reactable_removed_) {
- delete reactable;
- }
- }
- }
-}
-
-void Reactor::Stop() {
- if (!is_running_) {
- LOG(WARNING) << __func__ << ": not running, will stop once it's started";
- }
- auto control = eventfd_write(control_fd_, 1);
- CHECK_NE(control, -1) << __func__ << ": failed: " << strerror(errno);
-}
-
-Reactor::Reactable* Reactor::Register(int fd, Closure on_read_ready, Closure on_write_ready) {
- uint32_t poll_event_type = 0;
- if (on_read_ready != nullptr) {
- poll_event_type |= (EPOLLIN | EPOLLRDHUP);
- }
- if (on_write_ready != nullptr) {
- poll_event_type |= EPOLLOUT;
- }
- auto* reactable = new Reactable(fd, on_read_ready, on_write_ready);
- epoll_event event = {
- .events = poll_event_type,
- {.ptr = reactable}
- };
- int register_fd;
- RUN_NO_INTR(register_fd = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event));
- CHECK_NE(register_fd, -1) << __func__ << ": failed: " << strerror(errno);
- return reactable;
-}
-
-void Reactor::Unregister(Reactor::Reactable* reactable) {
- CHECK_NE(reactable, nullptr);
- {
- std::lock_guard<std::mutex> lock(mutex_);
- invalidation_list_.push_back(reactable);
- }
- {
- int result;
- std::lock_guard<std::recursive_mutex> reactable_lock(reactable->lock_);
- RUN_NO_INTR(result = epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, reactable->fd_, nullptr));
- if (result == -1 && errno == ENOENT) {
- LOG(INFO) << __func__ << ": reactable is invalid or unregistered";
- } else if (result == -1) {
- PLOG(FATAL) << __func__ << ": failed";
- }
- // If we are unregistering during the callback event from this reactable, we delete it after the callback is executed.
- // reactable->is_executing_ is protected by reactable->lock_, so it's thread safe.
- if (reactable->is_executing_) {
- reactable_removed_ = true;
- }
- }
- // If we are unregistering outside of the callback event from this reactable, we delete it now
- if (!reactable_removed_) {
- delete reactable;
- }
-}
-
-void Reactor::ModifyRegistration(Reactor::Reactable* reactable, Closure on_read_ready, Closure on_write_ready) {
- CHECK_NE(reactable, nullptr);
-
- uint32_t poll_event_type = 0;
- if (on_read_ready != nullptr) {
- poll_event_type |= (EPOLLIN | EPOLLRDHUP);
- }
- if (on_write_ready != nullptr) {
- poll_event_type |= EPOLLOUT;
- }
- {
- std::lock_guard<std::recursive_mutex> reactable_lock(reactable->lock_);
- reactable->on_read_ready_ = std::move(on_read_ready);
- reactable->on_write_ready_ = std::move(on_write_ready);
- }
- epoll_event event = {
- .events = poll_event_type,
- {.ptr = reactable}
- };
- int modify_fd;
- RUN_NO_INTR(modify_fd = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, reactable->fd_, &event));
- CHECK_NE(modify_fd, -1) << __func__ << ": failed: " << strerror(errno);
-}
-
-} // namespace common
-} // namespace bluetooth
+++ /dev/null
-/*
- * 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.
- */
-
-#pragma once
-
-#include <sys/epoll.h>
-#include <atomic>
-#include <functional>
-#include <list>
-#include <mutex>
-#include <thread>
-
-#include "common/utils.h"
-
-namespace bluetooth {
-namespace common {
-
-// Format of closure to be used in the entire stack
-using Closure = std::function<void()>;
-
-// A simple implementation of reactor-style looper.
-// When a reactor is running, the main loop is polling and blocked until at least one registered reactable is ready to
-// read or write. It will invoke on_read_ready() or on_write_ready(), which is registered with the reactor. Then, it
-// blocks again until ready event.
-class Reactor {
- public:
- // An object used for Unregister() and ModifyRegistration()
- class Reactable;
-
- // Construct a reactor on the current thread
- Reactor();
-
- // Destruct this reactor and release its resources
- ~Reactor();
-
- DISALLOW_COPY_AND_ASSIGN(Reactor);
-
- // Start the reactor. The current thread will be blocked until Stop() is invoked and handled.
- void Run();
-
- // Stop the reactor. Must be invoked from a different thread. Note: all registered reactables will not be unregistered
- // by Stop(). If the reactor is not running, it will be stopped once it's started.
- void Stop();
-
- // Register a reactable fd to this reactor. Returns a pointer to a Reactable. Caller must use this object to
- // unregister or modify registration. Ownership of the memory space is NOT transferred to user.
- Reactable* Register(int fd, Closure on_read_ready, Closure on_write_ready);
-
- // Unregister a reactable from this reactor
- void Unregister(Reactable* reactable);
-
- // Modify the registration for a reactable with given reactable
- void ModifyRegistration(Reactable* reactable, Closure on_read_ready, Closure on_write_ready);
-
- private:
- mutable std::mutex mutex_;
- int epoll_fd_;
- int control_fd_;
- std::atomic<bool> is_running_;
- std::list<Reactable*> invalidation_list_;
- bool reactable_removed_;
-};
-
-} // namespace common
-} // namespace bluetooth
+++ /dev/null
-/*
- * 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 "reactor.h"
-
-#include <sys/eventfd.h>
-#include <chrono>
-#include <future>
-#include <thread>
-
-#include "base/logging.h"
-#include "gtest/gtest.h"
-
-namespace bluetooth {
-namespace common {
-namespace {
-
-constexpr int kReadReadyValue = 100;
-
-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_, 0);
- }
-
- ~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_, 0);
- }
-
- FakeReactable(Reactor* reactor) : fd_(eventfd(0, 0)), reactor_(reactor) {
- EXPECT_NE(fd_, 0);
- }
-
- ~FakeReactable() {
- close(fd_);
- }
-
- void OnReadReady() {
- uint64_t value = 0;
- auto read_result = eventfd_read(fd_, &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_, [this] { this->sample_reactable_.OnReadReady(); },
- [this] { this->sample_reactable_.OnWriteReady(); });
- 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);
- }
-
- SampleReactable sample_reactable_;
- Reactor::Reactable* reactable_ = nullptr;
- int fd_;
-
- private:
- Reactor* reactor_;
- uint64_t output_data_ = kSampleOutputValue;
-};
-
-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_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr);
-
- reactor_->Unregister(reactable);
-}
-
-TEST_F(ReactorTest, cold_register) {
- FakeReactable fake_reactable;
- auto* reactable =
- reactor_->Register(fake_reactable.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr);
- 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_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr);
- 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_unregister_from_different_thread) {
- FakeReactable fake_reactable;
- auto* reactable =
- reactor_->Register(fake_reactable.fd_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr);
- 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_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr);
- auto write_result = eventfd_write(fake_reactable.fd_, FakeReactable::kRegisterSampleReactable);
- EXPECT_EQ(write_result, 0);
- EXPECT_EQ(future.get(), kReadReadyValue);
- 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_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr);
- 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);
- EXPECT_EQ(future.get(), kReadReadyValue);
- reactor_->Stop();
- reactor_thread.join();
- LOG(INFO);
-
- reactor_->Unregister(reactable);
- LOG(INFO);
-}
-
-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_, nullptr, std::bind(&FakeReactable::OnWriteReady, &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_, std::bind(&FakeReactable::OnReadReady, &fake_reactable), nullptr);
- reactor_->ModifyRegistration(reactable, nullptr, std::bind(&FakeReactable::OnWriteReady, &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();
-}
-
-} // namespace
-} // namespace common
-} // namespace bluetooth
+++ /dev/null
-/*
- * 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 "repeating_alarm.h"
-
-#include <sys/timerfd.h>
-#include <cstring>
-
-#include "base/logging.h"
-#include "utils.h"
-
-namespace bluetooth {
-namespace common {
-
-RepeatingAlarm::RepeatingAlarm(Thread* thread)
- : thread_(thread),
- fd_(timerfd_create(CLOCK_BOOTTIME_ALARM, 0)) {
- CHECK_NE(fd_, -1) << __func__ << ": cannot create timerfd: " << strerror(errno);
-
- token_ = thread_->GetReactor()->Register(fd_, [this] { on_fire(); }, nullptr);
-}
-
-RepeatingAlarm::~RepeatingAlarm() {
- thread_->GetReactor()->Unregister(token_);
-
- int close_status;
- RUN_NO_INTR(close_status = close(fd_));
- CHECK_NE(close_status, -1) << __func__ << ": cannot close timerfd: " << strerror(errno);
-}
-
-void RepeatingAlarm::Schedule(Closure task, std::chrono::milliseconds period) {
- std::lock_guard<std::mutex> lock(mutex_);
- long period_ms = period.count();
- itimerspec timer_itimerspec{
- {period_ms / 1000, period_ms % 1000 * 1000000},
- {period_ms / 1000, period_ms % 1000 * 1000000}
- };
- int result = timerfd_settime(fd_, 0, &timer_itimerspec, nullptr);
- CHECK_EQ(result, 0) << __func__ << ": failed, error=" << strerror(errno);
-
- task_ = std::move(task);
-}
-
-void RepeatingAlarm::Cancel() {
- std::lock_guard<std::mutex> lock(mutex_);
- itimerspec disarm_itimerspec{/* disarm timer */};
- int result = timerfd_settime(fd_, 0, &disarm_itimerspec, nullptr);
- CHECK_EQ(result, 0) << __func__ << ": failed, error=" << strerror(errno);
-}
-
-void RepeatingAlarm::on_fire() {
- std::unique_lock<std::mutex> lock(mutex_);
- auto task = task_;
- uint64_t times_invoked;
- auto bytes_read = read(fd_, ×_invoked, sizeof(uint64_t));
- lock.unlock();
- task();
- CHECK_EQ(bytes_read, static_cast<ssize_t>(sizeof(uint64_t))) << __func__ << ": failed, error=" << strerror(errno);
-}
-
-} // namespace common
-} // namespace bluetooth
+++ /dev/null
-/*
- * 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.
- */
-
-#pragma once
-
-#include <functional>
-#include <memory>
-#include <mutex>
-
-#include "common/thread.h"
-#include "common/utils.h"
-
-namespace bluetooth {
-namespace common {
-
-// A repeating alarm for reactor-based thread, implemented by Linux timerfd.
-// When it's constructed, it will register a reactable on the specified thread; when it's destroyed, it will unregister
-// itself from the thread.
-class RepeatingAlarm {
- public:
- // Create and register a repeating alarm on given thread
- explicit RepeatingAlarm(Thread* thread);
-
- // Unregister this alarm from the thread and release resource
- ~RepeatingAlarm();
-
- DISALLOW_COPY_AND_ASSIGN(RepeatingAlarm);
-
- // Schedule a repeating alarm with given period
- void Schedule(Closure task, std::chrono::milliseconds period);
-
- // Cancel the alarm. No-op if it's not armed.
- void Cancel();
-
- private:
- Closure task_;
- Thread* thread_;
- int fd_ = 0;
- Reactor::Reactable* token_;
- mutable std::mutex mutex_;
- void on_fire();
-};
-
-} // namespace common
-} // namespace bluetooth
+++ /dev/null
-/*
- * 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 "repeating_alarm.h"
-
-#include <future>
-
-#include "base/logging.h"
-#include "gtest/gtest.h"
-
-namespace bluetooth {
-namespace common {
-namespace {
-
-constexpr int error_ms = 20;
-
-class RepeatingAlarmTest : public ::testing::Test {
- protected:
- void SetUp() override {
- thread_ = new Thread("test_thread", Thread::Priority::NORMAL);
- alarm_ = new RepeatingAlarm(thread_);
- }
-
- void TearDown() override {
- delete alarm_;
- delete thread_;
- }
-
- void VerifyMultipleDelayedTasks(int scheduled_tasks, int task_length_ms, int interval_between_tasks_ms) {
- std::promise<void> promise;
- auto future = promise.get_future();
- auto start_time = std::chrono::steady_clock::now();
- int counter = 0;
- alarm_->Schedule(
- [&counter, &promise, start_time, scheduled_tasks, task_length_ms, interval_between_tasks_ms]() {
- counter++;
- auto time_now = std::chrono::steady_clock::now();
- auto time_delta = time_now - start_time;
- if (counter == scheduled_tasks) {
- promise.set_value();
- }
- ASSERT_NEAR(time_delta.count(), interval_between_tasks_ms * 1000000 * counter, error_ms * 1000000);
- std::this_thread::sleep_for(std::chrono::milliseconds(task_length_ms));
- },
- std::chrono::milliseconds(interval_between_tasks_ms));
- future.get();
- alarm_->Cancel();
- }
-
- RepeatingAlarm* alarm_;
-
- private:
- Thread* thread_;
-};
-
-TEST_F(RepeatingAlarmTest, cancel_while_not_armed) {
- alarm_->Cancel();
-}
-
-TEST_F(RepeatingAlarmTest, schedule) {
- std::promise<void> promise;
- auto future = promise.get_future();
- auto before = std::chrono::steady_clock::now();
- int period_ms = 10;
- alarm_->Schedule([&promise]() { promise.set_value(); }, std::chrono::milliseconds(period_ms));
- future.get();
- alarm_->Cancel();
- auto after = std::chrono::steady_clock::now();
- auto duration = after - before;
- ASSERT_NEAR(duration.count(), period_ms * 1000000, error_ms * 1000000);
-}
-
-TEST_F(RepeatingAlarmTest, cancel_alarm) {
- alarm_->Schedule([]() { LOG(FATAL) << "Should not happen"; }, std::chrono::milliseconds(1));
- alarm_->Cancel();
- std::this_thread::sleep_for(std::chrono::milliseconds(5));
-}
-
-TEST_F(RepeatingAlarmTest, cancel_alarm_from_callback) {
- alarm_->Schedule([this]() { this->alarm_->Cancel(); }, std::chrono::milliseconds(1));
- std::this_thread::sleep_for(std::chrono::milliseconds(5));
-}
-
-TEST_F(RepeatingAlarmTest, schedule_while_alarm_armed) {
- alarm_->Schedule([]() { LOG(FATAL) << "Should not happen"; }, std::chrono::milliseconds(1));
- std::promise<void> promise;
- auto future = promise.get_future();
- alarm_->Schedule([&promise]() { promise.set_value(); }, std::chrono::milliseconds(10));
- future.get();
- alarm_->Cancel();
-}
-
-TEST_F(RepeatingAlarmTest, delete_while_alarm_armed) {
- alarm_->Schedule([]() { LOG(FATAL) << "Should not happen"; }, std::chrono::milliseconds(1));
- delete alarm_;
- alarm_ = nullptr;
- std::this_thread::sleep_for(std::chrono::milliseconds(1));
-}
-
-TEST_F(RepeatingAlarmTest, verify_small) {
- VerifyMultipleDelayedTasks(100, 1, 10);
-}
-
-TEST_F(RepeatingAlarmTest, verify_large) {
- VerifyMultipleDelayedTasks(100, 3, 10);
-}
-
-} // namespace
-} // namespace common
-} // namespace bluetooth
+++ /dev/null
-/*
- * 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 "common/thread.h"
-
-#include <fcntl.h>
-#include <sys/syscall.h>
-#include <cerrno>
-#include <cstring>
-
-#include "base/logging.h"
-
-namespace bluetooth {
-namespace common {
-
-namespace {
-constexpr int kRealTimeFifoSchedulingPriority = 1;
-}
-
-Thread::Thread(const std::string& name, const Priority priority)
- : name_(name),
- reactor_(),
- running_thread_(&Thread::run, this, priority) {}
-
-void Thread::run(Priority priority) {
- if (priority == Priority::REAL_TIME) {
- struct sched_param rt_params = {.sched_priority = kRealTimeFifoSchedulingPriority};
- auto linux_tid = static_cast<pid_t>(syscall(SYS_gettid));
- int rc;
- RUN_NO_INTR(rc = sched_setscheduler(linux_tid, SCHED_FIFO, &rt_params));
- if (rc != 0) {
- PLOG(ERROR) << __func__ << ": unable to set SCHED_FIFO priority";
- }
- }
- reactor_.Run();
-}
-
-Thread::~Thread() {
- Stop();
-}
-
-bool Thread::Stop() {
- std::lock_guard<std::mutex> lock(mutex_);
- CHECK_NE(std::this_thread::get_id(), running_thread_.get_id());
-
- if (!running_thread_.joinable()) {
- return false;
- }
- reactor_.Stop();
- running_thread_.join();
- return true;
-}
-
-bool Thread::IsSameThread() const {
- return std::this_thread::get_id() == running_thread_.get_id();
-}
-
-Reactor* Thread::GetReactor() const {
- return &reactor_;
-}
-
-std::string Thread::GetThreadName() const {
- return name_;
-}
-
-std::string Thread::ToString() const {
- return "Thread " + name_;
-}
-
-} // namespace common
-} // namespace bluetooth
+++ /dev/null
-/*
- * 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.
- */
-
-#pragma once
-
-#include <mutex>
-#include <string>
-#include <thread>
-
-#include "common/reactor.h"
-#include "common/utils.h"
-
-namespace bluetooth {
-namespace common {
-
-// Reactor-based looper thread implementation. The thread runs immediately after it is constructed, and stops after
-// Stop() is invoked. To assign task to this thread, user needs to register a reactable object to the underlying
-// reactor.
-class Thread {
- public:
- // Used by thread constructor. Suggest the priority to the kernel scheduler. Use REAL_TIME if we need (soft) real-time
- // scheduling guarantee for this thread; use NORMAL if no real-time guarantee is needed to save CPU time slice for
- // other threads
- enum class Priority {
- REAL_TIME,
- NORMAL,
- };
-
- // name: thread name for POSIX systems
- // priority: priority for kernel scheduler
- Thread(const std::string& name, Priority priority);
-
- // Stop and destroy this thread
- ~Thread();
-
- DISALLOW_COPY_AND_ASSIGN(Thread);
-
- // Stop this thread. Must be invoked from another thread. After this thread is stopped, it cannot be started again.
- bool Stop();
-
- // Return true if this function is invoked from this thread
- bool IsSameThread() const;
-
- // Return the POSIX thread name
- std::string GetThreadName() const;
-
- // Return a user-friendly string representation of this thread object
- std::string ToString() const;
-
- // Return the pointer of underlying reactor. The ownership is NOT transferred.
- Reactor* GetReactor() const;
-
- private:
- void run(Priority priority);
- mutable std::mutex mutex_;
- const std::string name_;
- mutable Reactor reactor_;
- std::thread running_thread_;
-};
-
-} // namespace common
-} // namespace bluetooth
+++ /dev/null
-/*
- * 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 "thread.h"
-
-#include <sys/eventfd.h>
-
-#include "base/logging.h"
-#include "gtest/gtest.h"
-#include "reactor.h"
-
-namespace bluetooth {
-namespace common {
-namespace {
-
-constexpr int kCheckIsSameThread = 1;
-
-class SampleReactable {
- public:
- explicit SampleReactable(Thread* thread) : thread_(thread), fd_(eventfd(0, 0)), is_same_thread_checked_(false) {
- EXPECT_NE(fd_, 0);
- }
-
- ~SampleReactable() {
- close(fd_);
- }
-
- void OnReadReady() {
- EXPECT_TRUE(thread_->IsSameThread());
- is_same_thread_checked_ = true;
- uint64_t val;
- eventfd_read(fd_, &val);
- }
-
- bool IsSameThreadCheckDone() {
- return is_same_thread_checked_;
- }
-
- Thread* thread_;
- int fd_;
- bool is_same_thread_checked_;
-};
-
-class ThreadTest : public ::testing::Test {
- protected:
- void SetUp() override {
- thread = new Thread("test", Thread::Priority::NORMAL);
- }
-
- void TearDown() override {
- delete thread;
- }
- Thread* thread = nullptr;
-};
-
-TEST_F(ThreadTest, just_stop_no_op) {
- thread->Stop();
-}
-
-TEST_F(ThreadTest, thread_name) {
- EXPECT_EQ(thread->GetThreadName(), "test");
-}
-
-TEST_F(ThreadTest, thread_to_string) {
- EXPECT_NE(thread->ToString().find("test"), std::string::npos);
-}
-
-TEST_F(ThreadTest, not_same_thread) {
- EXPECT_FALSE(thread->IsSameThread());
-}
-
-TEST_F(ThreadTest, same_thread) {
- Reactor* reactor = thread->GetReactor();
- SampleReactable sample_reactable(thread);
- auto* reactable =
- reactor->Register(sample_reactable.fd_, std::bind(&SampleReactable::OnReadReady, &sample_reactable), nullptr);
- int fd = sample_reactable.fd_;
- int write_result = eventfd_write(fd, kCheckIsSameThread);
- EXPECT_EQ(write_result, 0);
- while (!sample_reactable.IsSameThreadCheckDone()) std::this_thread::yield();
- reactor->Unregister(reactable);
-}
-
-} // namespace
-} // namespace common
-} // namespace bluetooth
cfi: false,
},
}
+
+cc_benchmark {
+ name: "bluetooth_benchmark_gd",
+ defaults: ["gd_defaults"],
+ host_supported: true,
+ srcs: [
+ "benchmark.cc",
+ ":BluetoothOsBenchmarkSources",
+ ],
+ static_libs : [
+ "libbluetooth_gd",
+ ],
+}
--- /dev/null
+/*
+ * 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 <benchmark/benchmark.h>
+
+int main(int argc, char** argv) {
+ ::benchmark::Initialize(&argc, argv);
+ if (::benchmark::ReportUnrecognizedArguments(argc, argv)) {
+ return 1;
+ }
+ ::benchmark::RunSpecifiedBenchmarks();
+}
"linux_generic/thread_unittest.cc",
]
}
+
+filegroup {
+ name: "BluetoothOsBenchmarkSources",
+ srcs: [
+ "alarm_benchmark.cc",
+ "thread_benchmark.cc",
+ ]
+}
--- /dev/null
+/*
+ * 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 <chrono>
+#include <future>
+#include <unordered_map>
+
+#include "benchmark/benchmark.h"
+
+#include "os/alarm.h"
+#include "os/repeating_alarm.h"
+#include "os/thread.h"
+
+using ::benchmark::State;
+using ::bluetooth::os::Alarm;
+using ::bluetooth::os::RepeatingAlarm;
+using ::bluetooth::os::Thread;
+
+class BM_ReactableAlarm : public ::benchmark::Fixture {
+ protected:
+ void SetUp(State& st) override {
+ ::benchmark::Fixture::SetUp(st);
+ thread_ = std::make_unique<Thread>("timer_benchmark", Thread::Priority::REAL_TIME);
+ alarm_ = std::make_unique<Alarm>(thread_.get());
+ repeating_alarm_ = std::make_unique<RepeatingAlarm>(thread_.get());
+ map_.clear();
+ scheduled_tasks_ = 0;
+ task_length_ = 0;
+ task_interval_ = 0;
+ task_counter_ = 0;
+ promise_ = std::promise<void>();
+ }
+
+ void TearDown(State& st) override {
+ alarm_ = nullptr;
+ repeating_alarm_ = nullptr;
+ thread_->Stop();
+ thread_ = nullptr;
+ ::benchmark::Fixture::TearDown(st);
+ }
+
+ void AlarmSleepAndCountDelayedTime() {
+ auto end_time = std::chrono::steady_clock::now();
+ auto duration_since_start = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time_);
+ task_counter_++;
+ map_[duration_since_start.count() - task_counter_ * task_interval_]++;
+ std::this_thread::sleep_for(std::chrono::milliseconds(task_length_));
+ if (task_counter_ >= scheduled_tasks_) {
+ promise_.set_value();
+ }
+ }
+
+ void TimerFire() {
+ promise_.set_value();
+ }
+
+ int64_t scheduled_tasks_;
+ int64_t task_length_;
+ int64_t task_interval_;
+ int task_counter_;
+ std::unordered_map<int, int> map_;
+ std::promise<void> promise_;
+ std::chrono::time_point<std::chrono::steady_clock> start_time_;
+ std::unique_ptr<Thread> thread_;
+ std::unique_ptr<Alarm> alarm_;
+ std::unique_ptr<RepeatingAlarm> repeating_alarm_;
+};
+
+BENCHMARK_DEFINE_F(BM_ReactableAlarm, timer_performance_ms)(State& state) {
+ auto milliseconds = static_cast<int>(state.range(0));
+ for (auto _ : state) {
+ auto start_time_point = std::chrono::steady_clock::now();
+ alarm_->Schedule([this] { return TimerFire(); }, std::chrono::milliseconds(milliseconds));
+ promise_.get_future().get();
+ auto end_time_point = std::chrono::steady_clock::now();
+ auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time_point - start_time_point);
+ state.SetIterationTime(static_cast<double>(duration.count()) * 1e-6);
+ alarm_->Cancel();
+ }
+};
+
+BENCHMARK_REGISTER_F(BM_ReactableAlarm, timer_performance_ms)
+ ->Arg(1)
+ ->Arg(5)
+ ->Arg(10)
+ ->Arg(20)
+ ->Arg(100)
+ ->Arg(1000)
+ ->Arg(2000)
+ ->Iterations(1)
+ ->UseRealTime();
+
+BENCHMARK_DEFINE_F(BM_ReactableAlarm, periodic_accuracy)(State& state) {
+ for (auto _ : state) {
+ scheduled_tasks_ = state.range(0);
+ task_length_ = state.range(1);
+ task_interval_ = state.range(2);
+ start_time_ = std::chrono::steady_clock::now();
+ repeating_alarm_->Schedule([this] { AlarmSleepAndCountDelayedTime(); }, std::chrono::milliseconds(task_interval_));
+ promise_.get_future().get();
+ repeating_alarm_->Cancel();
+ }
+ for (const auto& delay : map_) {
+ state.counters[std::to_string(delay.first)] = delay.second;
+ }
+};
+
+BENCHMARK_REGISTER_F(BM_ReactableAlarm, periodic_accuracy)
+ ->Args({2000, 1, 5})
+ ->Args({2000, 3, 5})
+ ->Args({2000, 1, 7})
+ ->Args({2000, 3, 7})
+ ->Args({2000, 1, 20})
+ ->Args({2000, 5, 20})
+ ->Args({2000, 10, 20})
+ ->Args({2000, 15, 20})
+ ->Iterations(1)
+ ->UseRealTime();
#include <sys/eventfd.h>
#include <thread>
-#include <gtest/gtest.h>
+#include "gtest/gtest.h"
namespace bluetooth {
namespace os {
--- /dev/null
+/*
+ * 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 <future>
+#include <memory>
+#include <thread>
+
+#include "benchmark/benchmark.h"
+
+#include "os/handler.h"
+#include "os/thread.h"
+
+using ::benchmark::State;
+using ::bluetooth::os::Handler;
+using ::bluetooth::os::Thread;
+
+#define NUM_MESSAGES_TO_SEND 100000
+
+class BM_ThreadPerformance : public ::benchmark::Fixture {
+ protected:
+ void SetUp(State& st) override {
+ benchmark::Fixture::SetUp(st);
+ counter_promise_ = std::promise<void>();
+ counter_ = 0;
+ }
+ void TearDown(State& st) override {
+ benchmark::Fixture::TearDown(st);
+ }
+ void callback_batch() {
+ counter_++;
+ if (counter_ >= num_messages_to_send_) {
+ counter_promise_.set_value();
+ }
+ }
+
+ void callback() {
+ counter_promise_.set_value();
+ }
+
+ int64_t num_messages_to_send_;
+ int64_t counter_;
+ std::promise<void> counter_promise_;
+};
+
+class BM_ReactorThread : public BM_ThreadPerformance {
+ protected:
+ void SetUp(State& st) override {
+ BM_ThreadPerformance::SetUp(st);
+ thread_ = std::make_unique<Thread>("BM_ReactorThread thread", Thread::Priority::NORMAL);
+ handler_ = std::make_unique<Handler>(thread_.get());
+ }
+ void TearDown(State& st) override {
+ handler_ = nullptr;
+ thread_->Stop();
+ thread_ = nullptr;
+ BM_ThreadPerformance::TearDown(st);
+ }
+ std::unique_ptr<Thread> thread_;
+ std::unique_ptr<Handler> handler_;
+};
+
+BENCHMARK_DEFINE_F(BM_ReactorThread, batch_enque_dequeue)(State& state) {
+ for (auto _ : state) {
+ num_messages_to_send_ = state.range(0);
+ counter_ = 0;
+ counter_promise_ = std::promise<void>();
+ std::future<void> counter_future = counter_promise_.get_future();
+ for (int i = 0; i < num_messages_to_send_; i++) {
+ handler_->Post([this]() { callback_batch(); });
+ }
+ counter_future.wait();
+ }
+};
+
+BENCHMARK_REGISTER_F(BM_ReactorThread, batch_enque_dequeue)
+ ->Arg(10)
+ ->Arg(1000)
+ ->Arg(10000)
+ ->Arg(100000)
+ ->Iterations(1)
+ ->UseRealTime();
+
+BENCHMARK_DEFINE_F(BM_ReactorThread, sequential_execution)(State& state) {
+ for (auto _ : state) {
+ num_messages_to_send_ = state.range(0);
+ for (int i = 0; i < num_messages_to_send_; i++) {
+ counter_promise_ = std::promise<void>();
+ std::future<void> counter_future = counter_promise_.get_future();
+ handler_->Post([this]() { callback(); });
+ counter_future.wait();
+ }
+ }
+};
+
+BENCHMARK_REGISTER_F(BM_ReactorThread, sequential_execution)
+ ->Arg(10)
+ ->Arg(1000)
+ ->Arg(10000)
+ ->Arg(100000)
+ ->Iterations(1)
+ ->UseRealTime();