diff options
author | 2018-12-03 16:50:40 +0100 | |
---|---|---|
committer | 2018-12-03 22:28:48 +0100 | |
commit | cd985f72eefc695cdcac5927b5836d13bbdbfd9d (patch) | |
tree | 547973713c87b8b635e829de33875545e8bb60bf | |
parent | 680f457c58380080caad68d98fff5a9b695680d1 (diff) |
Split Timer into OnceTimer and RepeatingTimer
Follow the naming convention in libbase, where Callback was split into
OnceCallback, and RepeatingCallback, to make it obvious how many times
callback is expected to be called.
Test: unit tests were split from TimerTest into OnceTimerTest and
RepeatingTimerTest
Bug: 110303473
Change-Id: Ibbb9bb74a2e6e1a0f0f74c6ec44f63c5a4a1537f
-rw-r--r-- | system/btif/src/btif_a2dp_source.cc | 6 | ||||
-rw-r--r-- | system/common/Android.bp | 6 | ||||
-rw-r--r-- | system/common/benchmark/timer_performance_benchmark.cc | 44 | ||||
-rw-r--r-- | system/common/message_loop_thread.h | 3 | ||||
-rw-r--r-- | system/common/once_timer.cc | 130 | ||||
-rw-r--r-- | system/common/once_timer.h | 91 | ||||
-rw-r--r-- | system/common/once_timer_unittest.cc | 225 | ||||
-rw-r--r-- | system/common/repeating_timer.cc (renamed from system/common/timer.cc) | 71 | ||||
-rw-r--r-- | system/common/repeating_timer.h (renamed from system/common/timer.h) | 43 | ||||
-rw-r--r-- | system/common/repeating_timer_unittest.cc | 252 | ||||
-rw-r--r-- | system/common/timer_unittest.cc | 401 |
11 files changed, 762 insertions, 510 deletions
diff --git a/system/btif/src/btif_a2dp_source.cc b/system/btif/src/btif_a2dp_source.cc index 04c02bb8fd..47fb1a1392 100644 --- a/system/btif/src/btif_a2dp_source.cc +++ b/system/btif/src/btif_a2dp_source.cc @@ -41,8 +41,8 @@ #include "btif_util.h" #include "common/message_loop_thread.h" #include "common/metrics.h" +#include "common/repeating_timer.h" #include "common/time_util.h" -#include "common/timer.h" #include "osi/include/fixed_queue.h" #include "osi/include/log.h" #include "osi/include/osi.h" @@ -51,7 +51,7 @@ using bluetooth::common::A2dpSessionMetrics; using bluetooth::common::BluetoothMetricsLogger; -using bluetooth::common::Timer; +using bluetooth::common::RepeatingTimer; extern std::unique_ptr<tUIPC_STATE> a2dp_uipc; @@ -212,7 +212,7 @@ class BtifA2dpSource { fixed_queue_t* tx_audio_queue; bool tx_flush; /* Discards any outgoing data when true */ - Timer media_alarm; + RepeatingTimer media_alarm; const tA2DP_ENCODER_INTERFACE* encoder_interface; uint64_t encoder_interval_ms; /* Local copy of the encoder interval */ BtifMediaStats stats; diff --git a/system/common/Android.bp b/system/common/Android.bp index ee7bfbcedf..633d7ff81a 100644 --- a/system/common/Android.bp +++ b/system/common/Android.bp @@ -8,9 +8,10 @@ cc_library_static { ], srcs: [ "address_obfuscator.cc", - "timer.cc", "message_loop_thread.cc", "metrics.cc", + "once_timer.cc", + "repeating_timer.cc", "time_util.cc", ], shared_libs: [ @@ -32,10 +33,11 @@ cc_test { ], srcs : [ "address_obfuscator_unittest.cc", - "timer_unittest.cc", "leaky_bonded_queue_unittest.cc", "message_loop_thread_unittest.cc", "metrics_unittest.cc", + "once_timer_unittest.cc", + "repeating_timer_unittest.cc", "state_machine_unittest.cc", "time_util_unittest.cc", ], diff --git a/system/common/benchmark/timer_performance_benchmark.cc b/system/common/benchmark/timer_performance_benchmark.cc index a2f2c48217..deaaad8662 100644 --- a/system/common/benchmark/timer_performance_benchmark.cc +++ b/system/common/benchmark/timer_performance_benchmark.cc @@ -21,14 +21,16 @@ #include <future> #include "common/message_loop_thread.h" +#include "common/once_timer.h" +#include "common/repeating_timer.h" #include "common/time_util.h" -#include "common/timer.h" #include "osi/include/alarm.h" using ::benchmark::State; using bluetooth::common::MessageLoopThread; +using bluetooth::common::OnceTimer; +using bluetooth::common::RepeatingTimer; using bluetooth::common::time_get_os_boottime_us; -using bluetooth::common::Timer; // fake get_main_message_loop implementation for alarm base::MessageLoop* get_main_message_loop() { return nullptr; } @@ -104,14 +106,17 @@ class BM_AlarmTaskTimer : public ::benchmark::Fixture { message_loop_thread_ = new MessageLoopThread("timer_benchmark"); message_loop_thread_->StartUp(); message_loop_thread_->EnableRealTimeScheduling(); - timer_ = new Timer(); + once_timer_ = new OnceTimer(); + repeating_timer_ = new RepeatingTimer(); g_promise = std::make_shared<std::promise<void>>(); } void TearDown(State& st) override { g_promise = nullptr; - delete timer_; - timer_ = nullptr; + delete once_timer_; + once_timer_ = nullptr; + delete repeating_timer_; + repeating_timer_ = nullptr; message_loop_thread_->ShutDown(); delete message_loop_thread_; message_loop_thread_ = nullptr; @@ -119,18 +124,19 @@ class BM_AlarmTaskTimer : public ::benchmark::Fixture { } MessageLoopThread* message_loop_thread_; - Timer* timer_; + OnceTimer* once_timer_; + RepeatingTimer* repeating_timer_; }; BENCHMARK_DEFINE_F(BM_AlarmTaskTimer, 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(); - timer_->Schedule(message_loop_thread_->GetWeakPtr(), FROM_HERE, - base::Bind(&TimerFire, nullptr), - base::TimeDelta::FromMilliseconds(milliseconds)); + once_timer_->Schedule(message_loop_thread_->GetWeakPtr(), FROM_HERE, + base::BindOnce(&TimerFire, nullptr), + base::TimeDelta::FromMilliseconds(milliseconds)); g_promise->get_future().get(); - timer_->Cancel(); + once_timer_->Cancel(); auto end_time_point = time_get_os_boottime_us(); auto duration = end_time_point - start_time_point; state.SetIterationTime(duration * 1e-6); @@ -204,7 +210,8 @@ class BM_AlarmTaskPeriodicTimer : public ::benchmark::Fixture { message_loop_thread_ = new MessageLoopThread("timer_benchmark"); message_loop_thread_->StartUp(); message_loop_thread_->EnableRealTimeScheduling(); - timer_ = new Timer(); + once_timer_ = new OnceTimer(); + repeating_timer_ = new RepeatingTimer(); g_map.clear(); g_promise = std::make_shared<std::promise<void>>(); g_scheduled_tasks = 0; @@ -215,8 +222,10 @@ class BM_AlarmTaskPeriodicTimer : public ::benchmark::Fixture { void TearDown(State& st) override { g_promise = nullptr; - delete timer_; - timer_ = nullptr; + delete once_timer_; + once_timer_ = nullptr; + delete repeating_timer_; + repeating_timer_ = nullptr; message_loop_thread_->ShutDown(); delete message_loop_thread_; message_loop_thread_ = nullptr; @@ -224,7 +233,8 @@ class BM_AlarmTaskPeriodicTimer : public ::benchmark::Fixture { } MessageLoopThread* message_loop_thread_; - Timer* timer_; + OnceTimer* once_timer_; + RepeatingTimer* repeating_timer_; }; BENCHMARK_DEFINE_F(BM_AlarmTaskPeriodicTimer, periodic_accuracy) @@ -234,12 +244,12 @@ BENCHMARK_DEFINE_F(BM_AlarmTaskPeriodicTimer, periodic_accuracy) g_task_length = state.range(1); g_task_interval = state.range(2); g_start_time = time_get_os_boottime_us(); - timer_->SchedulePeriodic( + repeating_timer_->SchedulePeriodic( message_loop_thread_->GetWeakPtr(), FROM_HERE, - base::Bind(&AlarmSleepAndCountDelayedTime, nullptr), + base::BindRepeating(&AlarmSleepAndCountDelayedTime, nullptr), base::TimeDelta::FromMilliseconds(g_task_interval)); g_promise->get_future().get(); - timer_->Cancel(); + repeating_timer_->Cancel(); } for (const auto& delay : g_map) { state.counters[std::to_string(delay.first)] = delay.second; diff --git a/system/common/message_loop_thread.h b/system/common/message_loop_thread.h index b25f43862d..3984a9b58f 100644 --- a/system/common/message_loop_thread.h +++ b/system/common/message_loop_thread.h @@ -176,7 +176,8 @@ class MessageLoopThread final { bool DoInThreadDelayed(const base::Location& from_here, base::OnceClosure task, const base::TimeDelta& delay); - friend class Timer; // allow Timer to use DoInThreadDelayed() + friend class RepeatingTimer; // allow Timer to use DoInThreadDelayed() + friend class OnceTimer; // allow OnceTimer to use DoInThreadDelayed() /** * Actual method to run the thread, blocking until ShutDown() is called diff --git a/system/common/once_timer.cc b/system/common/once_timer.cc new file mode 100644 index 0000000000..413dc4447b --- /dev/null +++ b/system/common/once_timer.cc @@ -0,0 +1,130 @@ +/* + * 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. + */ + +#include "once_timer.h" + +#include "message_loop_thread.h" +#include "time_util.h" + +namespace bluetooth { + +namespace common { + +// This runs on user thread +OnceTimer::~OnceTimer() { + std::lock_guard<std::recursive_mutex> api_lock(api_mutex_); + if (message_loop_thread_ != nullptr && message_loop_thread_->IsRunning()) { + CancelAndWait(); + } +} + +// This runs on user thread +bool OnceTimer::Schedule(const base::WeakPtr<MessageLoopThread>& thread, + const base::Location& from_here, + base::OnceClosure task, base::TimeDelta delay) { + uint64_t time_now_us = time_get_os_boottime_us(); + uint64_t time_next_task_us = time_now_us + delay.InMicroseconds(); + std::lock_guard<std::recursive_mutex> api_lock(api_mutex_); + if (thread == nullptr) { + LOG(ERROR) << __func__ << ": thread must be non-null"; + return false; + } + CancelAndWait(); + task_ = std::move(task); + task_wrapper_.Reset( + base::BindOnce(&OnceTimer::RunTask, base::Unretained(this))); + message_loop_thread_ = thread; + delay_ = delay; + uint64_t time_until_next_us = time_next_task_us - time_get_os_boottime_us(); + if (!thread->DoInThreadDelayed( + from_here, task_wrapper_.callback(), + base::TimeDelta::FromMicroseconds(time_until_next_us))) { + LOG(ERROR) << __func__ + << ": failed to post task to message loop for thread " << *thread + << ", from " << from_here.ToString(); + task_wrapper_.Cancel(); + message_loop_thread_ = nullptr; + delay_ = {}; + return false; + } + return true; +} + +// This runs on user thread +void OnceTimer::Cancel() { + std::promise<void> promise; + CancelHelper(std::move(promise)); +} + +// This runs on user thread +void OnceTimer::CancelAndWait() { + std::promise<void> promise; + auto future = promise.get_future(); + CancelHelper(std::move(promise)); + future.wait(); +} + +// This runs on user thread +void OnceTimer::CancelHelper(std::promise<void> promise) { + std::lock_guard<std::recursive_mutex> api_lock(api_mutex_); + MessageLoopThread* scheduled_thread = message_loop_thread_.get(); + if (scheduled_thread == nullptr) { + promise.set_value(); + return; + } + if (scheduled_thread->GetThreadId() == base::PlatformThread::CurrentId()) { + CancelClosure(std::move(promise)); + return; + } + scheduled_thread->DoInThread( + FROM_HERE, base::BindOnce(&OnceTimer::CancelClosure, + base::Unretained(this), std::move(promise))); +} + +// This runs on message loop thread +void OnceTimer::CancelClosure(std::promise<void> promise) { + message_loop_thread_ = nullptr; + task_wrapper_.Cancel(); + std::move(task_); + delay_ = base::TimeDelta(); + promise.set_value(); +} + +// This runs on user thread +bool OnceTimer::IsScheduled() const { + std::lock_guard<std::recursive_mutex> api_lock(api_mutex_); + return message_loop_thread_ != nullptr && message_loop_thread_->IsRunning(); +} + +// This runs on message loop thread +void OnceTimer::RunTask() { + if (message_loop_thread_ == nullptr || !message_loop_thread_->IsRunning()) { + LOG(ERROR) << __func__ + << ": message_loop_thread_ is null or is not running"; + return; + } + CHECK_EQ(message_loop_thread_->GetThreadId(), + base::PlatformThread::CurrentId()) + << ": task must run on message loop thread"; + + task_wrapper_.Cancel(); + std::move(task_).Run(); + message_loop_thread_ = nullptr; +} + +} // namespace common + +} // namespace bluetooth diff --git a/system/common/once_timer.h b/system/common/once_timer.h new file mode 100644 index 0000000000..9968be5cd7 --- /dev/null +++ b/system/common/once_timer.h @@ -0,0 +1,91 @@ +/* + * 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. + */ + +#pragma once + +#include <base/bind.h> +#include <base/cancelable_callback.h> +#include <base/location.h> +#include <future> + +namespace bluetooth { + +namespace common { + +class MessageLoopThread; + +/** + * An alarm clock that posts a delayed task to a specified MessageLoopThread + * once. + * + * Warning: MessageLoopThread must be running when any task is scheduled or + * being executed + */ +class OnceTimer final { + public: + OnceTimer() {} + ~OnceTimer(); + + /** + * Schedule a delayed task to the MessageLoopThread. Only one task can be + * scheduled at a time. If another task is scheduled, it will cancel the + * previous task synchronously and schedule the new task; this blocks until + * the previous task is cancelled. + * + * @param thread thread to run the task + * @param from_here location where this task is originated + * @param task task created through base::Bind() + * @param delay delay for the task to be executed + * @return true iff task is scheduled successfully + */ + bool Schedule(const base::WeakPtr<MessageLoopThread>& thread, + const base::Location& from_here, base::OnceClosure task, + base::TimeDelta delay); + + /** + * Post an event which cancels the current task asynchronously + */ + void Cancel(); + + /** + * Post an event which cancels the current task and wait for the cancellation + * to be completed + */ + void CancelAndWait(); + + /** + * Returns true when there is a pending task scheduled on a running thread, + * otherwise false. + */ + bool IsScheduled() const; + + private: + base::WeakPtr<MessageLoopThread> message_loop_thread_; + base::CancelableOnceClosure task_wrapper_; + base::OnceClosure task_; + base::TimeDelta delay_; + mutable std::recursive_mutex api_mutex_; + void CancelHelper(std::promise<void> promise); + void CancelClosure(std::promise<void> promise); + + void RunTask(); + + DISALLOW_COPY_AND_ASSIGN(OnceTimer); +}; + +} // namespace common + +} // namespace bluetooth diff --git a/system/common/once_timer_unittest.cc b/system/common/once_timer_unittest.cc new file mode 100644 index 0000000000..d0fa238803 --- /dev/null +++ b/system/common/once_timer_unittest.cc @@ -0,0 +1,225 @@ +/* + * 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. + */ + +#include <base/bind.h> +#include <base/bind_helpers.h> +#include <base/logging.h> +#include <gtest/gtest.h> +#include <future> + +#include "message_loop_thread.h" +#include "once_timer.h" + +using bluetooth::common::MessageLoopThread; +using bluetooth::common::OnceTimer; + +// Allowed error between the expected and actual delay for DoInThreadDelayed(). +constexpr uint32_t delay_error_ms = 3; + +/** + * Unit tests to verify Task Scheduler. + */ +class OnceTimerTest : public ::testing::Test { + public: + void ShouldNotHappen() { FAIL() << "Should not happen"; } + + void IncreaseTaskCounter(int scheduled_tasks, std::promise<void>* promise) { + counter_++; + if (counter_ == scheduled_tasks) { + promise->set_value(); + } + } + + void GetName(std::string* name, std::promise<void>* promise) { + char my_name[256]; + pthread_getname_np(pthread_self(), my_name, sizeof(my_name)); + name->append(my_name); + promise->set_value(); + } + + void SleepAndIncreaseCounter(std::promise<void>* promise, int sleep_ms) { + promise->set_value(); + std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms)); + counter_++; + } + + void CancelTimerAndWait() { timer_->CancelAndWait(); } + + protected: + void SetUp() override { + ::testing::Test::SetUp(); + counter_ = 0; + timer_ = new OnceTimer(); + promise_ = new std::promise<void>(); + } + + void TearDown() override { + if (promise_ != nullptr) { + delete promise_; + promise_ = nullptr; + } + if (timer_ != nullptr) { + delete timer_; + timer_ = nullptr; + } + } + + int counter_; + OnceTimer* timer_; + std::promise<void>* promise_; +}; + +TEST_F(OnceTimerTest, initial_is_not_scheduled) { + ASSERT_FALSE(timer_->IsScheduled()); +} + +TEST_F(OnceTimerTest, schedule_task) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + auto future = promise_->get_future(); + std::string my_name; + uint32_t delay_ms = 5; + + timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE, + base::BindOnce(&OnceTimerTest::GetName, + base::Unretained(this), &my_name, promise_), + base::TimeDelta::FromMilliseconds(delay_ms)); + EXPECT_TRUE(timer_->IsScheduled()); + future.get(); + ASSERT_EQ(name, my_name); + std::this_thread::sleep_for(std::chrono::milliseconds(delay_error_ms)); + EXPECT_FALSE(timer_->IsScheduled()); +} + +TEST_F(OnceTimerTest, cancel_without_scheduling) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + + EXPECT_FALSE(timer_->IsScheduled()); + timer_->CancelAndWait(); + EXPECT_FALSE(timer_->IsScheduled()); +} + +TEST_F(OnceTimerTest, cancel_in_callback_no_deadlock) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + uint32_t delay_ms = 5; + + timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE, + base::BindOnce(&OnceTimerTest::CancelTimerAndWait, + base::Unretained(this)), + base::TimeDelta::FromMilliseconds(delay_ms)); +} + +TEST_F(OnceTimerTest, cancel_single_task) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + uint32_t delay_ms = 100000000; + timer_->Schedule( + message_loop_thread.GetWeakPtr(), FROM_HERE, + base::BindOnce(&OnceTimerTest::ShouldNotHappen, base::Unretained(this)), + base::TimeDelta::FromMilliseconds(delay_ms)); + std::this_thread::sleep_for(std::chrono::milliseconds(delay_error_ms)); + timer_->CancelAndWait(); +} + +TEST_F(OnceTimerTest, message_loop_thread_down_cancel_task) { + std::string name = "test_thread"; + { + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + uint32_t delay_ms = 100000000; + timer_->Schedule( + message_loop_thread.GetWeakPtr(), FROM_HERE, + base::BindOnce(&OnceTimerTest::ShouldNotHappen, base::Unretained(this)), + base::TimeDelta::FromMilliseconds(delay_ms)); + std::this_thread::sleep_for(std::chrono::milliseconds(delay_error_ms)); + } +} + +// Verify that if a task is being executed, then cancelling it is no-op +TEST_F(OnceTimerTest, cancel_current_task_no_effect) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + auto future = promise_->get_future(); + uint32_t delay_ms = 5; + + timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE, + base::BindOnce(&OnceTimerTest::SleepAndIncreaseCounter, + base::Unretained(this), promise_, delay_ms), + base::TimeDelta::FromMilliseconds(delay_ms)); + EXPECT_TRUE(timer_->IsScheduled()); + future.get(); + timer_->CancelAndWait(); + ASSERT_EQ(counter_, 1); + EXPECT_FALSE(timer_->IsScheduled()); +} + +TEST_F(OnceTimerTest, reschedule_task_dont_invoke_new_task_early) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + uint32_t delay_ms = 5; + timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE, + base::DoNothing(), + base::TimeDelta::FromMilliseconds(delay_ms)); + std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms - 2)); + timer_->Schedule( + message_loop_thread.GetWeakPtr(), FROM_HERE, + base::BindOnce(&OnceTimerTest::ShouldNotHappen, base::Unretained(this)), + base::TimeDelta::FromMilliseconds(delay_ms + 1000)); + std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); +} + +TEST_F(OnceTimerTest, reschedule_task_when_firing_dont_invoke_new_task_early) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + uint32_t delay_ms = 5; + timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE, + base::DoNothing(), + base::TimeDelta::FromMilliseconds(delay_ms)); + std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); + timer_->Schedule( + message_loop_thread.GetWeakPtr(), FROM_HERE, + base::BindOnce(&OnceTimerTest::ShouldNotHappen, base::Unretained(this)), + base::TimeDelta::FromMilliseconds(delay_ms + 1000)); + std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); +} + +TEST_F(OnceTimerTest, reschedule_task_when_firing_must_schedule_new_task) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + uint32_t delay_ms = 5; + std::string my_name; + auto future = promise_->get_future(); + timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE, + base::DoNothing(), + base::TimeDelta::FromMilliseconds(delay_ms)); + std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); + timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE, + base::BindOnce(&OnceTimerTest::GetName, + base::Unretained(this), &my_name, promise_), + base::TimeDelta::FromMilliseconds(delay_ms)); + future.get(); + ASSERT_EQ(name, my_name); +} diff --git a/system/common/timer.cc b/system/common/repeating_timer.cc index f0037d1287..e34dabc45c 100644 --- a/system/common/timer.cc +++ b/system/common/repeating_timer.cc @@ -14,7 +14,8 @@ * limitations under the License. */ -#include "timer.h" +#include "repeating_timer.h" + #include "message_loop_thread.h" #include "time_util.h" @@ -25,7 +26,7 @@ namespace common { constexpr base::TimeDelta kMinimumPeriod = base::TimeDelta::FromMicroseconds(1); // This runs on user thread -Timer::~Timer() { +RepeatingTimer::~RepeatingTimer() { std::lock_guard<std::recursive_mutex> api_lock(api_mutex_); if (message_loop_thread_ != nullptr && message_loop_thread_->IsRunning()) { CancelAndWait(); @@ -33,30 +34,17 @@ Timer::~Timer() { } // This runs on user thread -bool Timer::Schedule(const base::WeakPtr<MessageLoopThread>& thread, - const base::Location& from_here, base::Closure task, - base::TimeDelta delay) { - return ScheduleTaskHelper(thread, from_here, std::move(task), delay, false); -} - -// This runs on user thread -bool Timer::SchedulePeriodic(const base::WeakPtr<MessageLoopThread>& thread, - const base::Location& from_here, - base::Closure task, base::TimeDelta period) { +bool RepeatingTimer::SchedulePeriodic( + const base::WeakPtr<MessageLoopThread>& thread, + const base::Location& from_here, base::Closure task, + base::TimeDelta period) { if (period < kMinimumPeriod) { LOG(ERROR) << __func__ << ": period must be at least " << kMinimumPeriod; return false; } - return ScheduleTaskHelper(thread, from_here, std::move(task), period, true); -} -// This runs on user thread -bool Timer::ScheduleTaskHelper(const base::WeakPtr<MessageLoopThread>& thread, - const base::Location& from_here, - base::Closure task, base::TimeDelta delay, - bool is_periodic) { uint64_t time_now_us = time_get_os_boottime_us(); - uint64_t time_next_task_us = time_now_us + delay.InMicroseconds(); + uint64_t time_next_task_us = time_now_us + period.InMicroseconds(); std::lock_guard<std::recursive_mutex> api_lock(api_mutex_); if (thread == nullptr) { LOG(ERROR) << __func__ << ": thread must be non-null"; @@ -65,10 +53,10 @@ bool Timer::ScheduleTaskHelper(const base::WeakPtr<MessageLoopThread>& thread, CancelAndWait(); expected_time_next_task_us_ = time_next_task_us; task_ = std::move(task); - task_wrapper_.Reset(base::Bind(&Timer::RunTask, base::Unretained(this))); + task_wrapper_.Reset( + base::Bind(&RepeatingTimer::RunTask, base::Unretained(this))); message_loop_thread_ = thread; - period_ = delay; - is_periodic_ = is_periodic; + period_ = period; uint64_t time_until_next_us = time_next_task_us - time_get_os_boottime_us(); if (!thread->DoInThreadDelayed( from_here, task_wrapper_.callback(), @@ -80,20 +68,19 @@ bool Timer::ScheduleTaskHelper(const base::WeakPtr<MessageLoopThread>& thread, task_wrapper_.Cancel(); message_loop_thread_ = nullptr; period_ = {}; - is_periodic_ = false; return false; } return true; } // This runs on user thread -void Timer::Cancel() { +void RepeatingTimer::Cancel() { std::promise<void> promise; CancelHelper(std::move(promise)); } // This runs on user thread -void Timer::CancelAndWait() { +void RepeatingTimer::CancelAndWait() { std::promise<void> promise; auto future = promise.get_future(); CancelHelper(std::move(promise)); @@ -101,7 +88,7 @@ void Timer::CancelAndWait() { } // This runs on user thread -void Timer::CancelHelper(std::promise<void> promise) { +void RepeatingTimer::CancelHelper(std::promise<void> promise) { std::lock_guard<std::recursive_mutex> api_lock(api_mutex_); MessageLoopThread* scheduled_thread = message_loop_thread_.get(); if (scheduled_thread == nullptr) { @@ -113,29 +100,28 @@ void Timer::CancelHelper(std::promise<void> promise) { return; } scheduled_thread->DoInThread( - FROM_HERE, base::BindOnce(&Timer::CancelClosure, base::Unretained(this), - std::move(promise))); + FROM_HERE, base::BindOnce(&RepeatingTimer::CancelClosure, + base::Unretained(this), std::move(promise))); } // This runs on message loop thread -void Timer::CancelClosure(std::promise<void> promise) { +void RepeatingTimer::CancelClosure(std::promise<void> promise) { message_loop_thread_ = nullptr; task_wrapper_.Cancel(); task_ = {}; period_ = base::TimeDelta(); - is_periodic_ = false; expected_time_next_task_us_ = 0; promise.set_value(); } // This runs on user thread -bool Timer::IsScheduled() const { +bool RepeatingTimer::IsScheduled() const { std::lock_guard<std::recursive_mutex> api_lock(api_mutex_); return message_loop_thread_ != nullptr && message_loop_thread_->IsRunning(); } // This runs on message loop thread -void Timer::RunTask() { +void RepeatingTimer::RunTask() { if (message_loop_thread_ == nullptr || !message_loop_thread_->IsRunning()) { LOG(ERROR) << __func__ << ": message_loop_thread_ is null or is not running"; @@ -144,15 +130,7 @@ void Timer::RunTask() { CHECK_EQ(message_loop_thread_->GetThreadId(), base::PlatformThread::CurrentId()) << ": task must run on message loop thread"; - if (is_periodic_) { - RunPeriodicTask(); - } else { - RunSingleTask(); - } -} -// This runs on message loop thread -void Timer::RunPeriodicTask() { int64_t period_us = period_.InMicroseconds(); expected_time_next_task_us_ += period_us; uint64_t time_now_us = time_get_os_boottime_us(); @@ -178,17 +156,6 @@ void Timer::RunPeriodicTask() { } } -// This runs on message loop thread -void Timer::RunSingleTask() { - base::OnceClosure current_task = std::move(task_); - task_wrapper_.Cancel(); - task_ = {}; - period_ = base::TimeDelta(); - expected_time_next_task_us_ = 0; - std::move(current_task).Run(); - message_loop_thread_ = nullptr; -} - } // namespace common } // namespace bluetooth diff --git a/system/common/timer.h b/system/common/repeating_timer.h index 5a02dd6643..b4c5108398 100644 --- a/system/common/timer.h +++ b/system/common/repeating_timer.h @@ -29,33 +29,15 @@ class MessageLoopThread; /** * An alarm clock that posts a delayed task to a specified MessageLoopThread - * once or periodically. + * periodically. * * Warning: MessageLoopThread must be running when any task is scheduled or * being executed */ -class Timer final { +class RepeatingTimer final { public: - Timer() : is_periodic_(false), expected_time_next_task_us_(0) {} - ~Timer(); - Timer(const Timer&) = delete; - Timer& operator=(const Timer&) = delete; - - /** - * Schedule a delayed task to the MessageLoopThread. Only one task can be - * scheduled at a time. If another task is scheduled, it will cancel the - * previous task synchronously and schedule the new task; this blocks until - * the previous task is cancelled. - * - * @param thread thread to run the task - * @param from_here location where this task is originated - * @param task task created through base::Bind() - * @param delay delay for the task to be executed - * @return true iff task is scheduled successfully - */ - bool Schedule(const base::WeakPtr<MessageLoopThread>& thread, - const base::Location& from_here, base::Closure task, - base::TimeDelta delay); + RepeatingTimer() : expected_time_next_task_us_(0) {} + ~RepeatingTimer(); /** * Schedule a delayed periodic task to the MessageLoopThread. Only one task @@ -70,8 +52,8 @@ class Timer final { * @return true iff task is scheduled successfully */ bool SchedulePeriodic(const base::WeakPtr<MessageLoopThread>& thread, - const base::Location& from_here, base::Closure task, - base::TimeDelta period); + const base::Location& from_here, + base::RepeatingClosure task, base::TimeDelta period); /** * Post an event which cancels the current task asynchronously @@ -93,23 +75,16 @@ class Timer final { private: base::WeakPtr<MessageLoopThread> message_loop_thread_; base::CancelableClosure task_wrapper_; - base::Closure task_; + base::RepeatingClosure task_; base::TimeDelta period_; - bool is_periodic_; uint64_t expected_time_next_task_us_; // Using clock boot time in time_util.h mutable std::recursive_mutex api_mutex_; - bool ScheduleTaskHelper(const base::WeakPtr<MessageLoopThread>& thread, - const base::Location& from_here, base::Closure task, - base::TimeDelta delay, bool is_periodic); void CancelHelper(std::promise<void> promise); void CancelClosure(std::promise<void> promise); - /** - * Wraps a task. It posts another same task if the scheduled task is periodic. - */ void RunTask(); - void RunSingleTask(); - void RunPeriodicTask(); + + DISALLOW_COPY_AND_ASSIGN(RepeatingTimer); }; } // namespace common diff --git a/system/common/repeating_timer_unittest.cc b/system/common/repeating_timer_unittest.cc new file mode 100644 index 0000000000..20d8d676b2 --- /dev/null +++ b/system/common/repeating_timer_unittest.cc @@ -0,0 +1,252 @@ +/* + * 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. + */ + +#include <base/bind.h> +#include <base/bind_helpers.h> +#include <base/logging.h> +#include <gtest/gtest.h> +#include <future> + +#include "message_loop_thread.h" +#include "repeating_timer.h" + +using bluetooth::common::MessageLoopThread; +using bluetooth::common::RepeatingTimer; + +// Allowed error between the expected and actual delay for DoInThreadDelayed(). +constexpr uint32_t delay_error_ms = 3; + +/** + * Unit tests to verify Task Scheduler. + */ +class RepeatingTimerTest : public ::testing::Test { + public: + void ShouldNotHappen() { FAIL() << "Should not happen"; } + + void IncreaseTaskCounter(int scheduled_tasks, std::promise<void>* promise) { + counter_++; + if (counter_ == scheduled_tasks) { + promise->set_value(); + } + } + + void GetName(std::string* name, std::promise<void>* promise) { + char my_name[256]; + pthread_getname_np(pthread_self(), my_name, sizeof(my_name)); + name->append(my_name); + promise->set_value(); + } + + void SleepAndIncreaseCounter(std::promise<void>* promise, int sleep_ms) { + promise->set_value(); + std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms)); + counter_++; + } + + void VerifyDelayTimeAndSleep(std::chrono::steady_clock::time_point start_time, + int interval_ms, int scheduled_tasks, + int task_length_ms, + std::promise<void>* promise) { + auto end_time = std::chrono::steady_clock::now(); + auto actual_delay = std::chrono::duration_cast<std::chrono::milliseconds>( + end_time - start_time); + counter_++; + int64_t scheduled_delay_ms = interval_ms * counter_; + if (counter_ == scheduled_tasks) { + promise->set_value(); + } + ASSERT_NEAR(scheduled_delay_ms, actual_delay.count(), delay_error_ms); + std::this_thread::sleep_for(std::chrono::milliseconds(task_length_ms)); + } + + void VerifyMultipleDelayedTasks(int scheduled_tasks, int task_length_ms, + int interval_between_tasks_ms) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + message_loop_thread.EnableRealTimeScheduling(); + auto future = promise_->get_future(); + auto start_time = std::chrono::steady_clock::now(); + timer_->SchedulePeriodic( + message_loop_thread.GetWeakPtr(), FROM_HERE, + base::BindRepeating(&RepeatingTimerTest::VerifyDelayTimeAndSleep, + base::Unretained(this), start_time, + interval_between_tasks_ms, scheduled_tasks, + task_length_ms, promise_), + base::TimeDelta::FromMilliseconds(interval_between_tasks_ms)); + future.get(); + timer_->CancelAndWait(); + } + + void CancelRepeatingTimerAndWait() { timer_->CancelAndWait(); } + + protected: + void SetUp() override { + ::testing::Test::SetUp(); + counter_ = 0; + timer_ = new RepeatingTimer(); + promise_ = new std::promise<void>(); + } + + void TearDown() override { + if (promise_ != nullptr) { + delete promise_; + promise_ = nullptr; + } + if (timer_ != nullptr) { + delete timer_; + timer_ = nullptr; + } + } + + int counter_; + RepeatingTimer* timer_; + std::promise<void>* promise_; +}; + +TEST_F(RepeatingTimerTest, initial_is_not_scheduled) { + ASSERT_FALSE(timer_->IsScheduled()); +} + +TEST_F(RepeatingTimerTest, cancel_without_scheduling) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + + EXPECT_FALSE(timer_->IsScheduled()); + timer_->CancelAndWait(); + EXPECT_FALSE(timer_->IsScheduled()); +} + +TEST_F(RepeatingTimerTest, periodic_run) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + auto future = promise_->get_future(); + uint32_t delay_ms = 5; + int num_tasks = 200; + + timer_->SchedulePeriodic( + message_loop_thread.GetWeakPtr(), FROM_HERE, + base::BindRepeating(&RepeatingTimerTest::IncreaseTaskCounter, + base::Unretained(this), num_tasks, promise_), + base::TimeDelta::FromMilliseconds(delay_ms)); + future.get(); + ASSERT_GE(counter_, num_tasks); + timer_->CancelAndWait(); +} + +TEST_F(RepeatingTimerTest, schedule_periodic_task_zero_interval) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + uint32_t interval_ms = 0; + + ASSERT_FALSE(timer_->SchedulePeriodic( + message_loop_thread.GetWeakPtr(), FROM_HERE, + base::BindRepeating(&RepeatingTimerTest::ShouldNotHappen, + base::Unretained(this)), + base::TimeDelta::FromMilliseconds(interval_ms))); + std::this_thread::sleep_for(std::chrono::milliseconds(delay_error_ms)); +} + +// Verify that deleting the timer without cancelling it will cancel the task +TEST_F(RepeatingTimerTest, periodic_delete_without_cancel) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + uint32_t delay_ms = 5; + timer_->SchedulePeriodic( + message_loop_thread.GetWeakPtr(), FROM_HERE, + base::BindRepeating(&RepeatingTimerTest::ShouldNotHappen, + base::Unretained(this)), + base::TimeDelta::FromMilliseconds(delay_ms)); + delete timer_; + timer_ = nullptr; + std::this_thread::sleep_for(std::chrono::milliseconds(delay_error_ms)); +} + +TEST_F(RepeatingTimerTest, cancel_single_task_near_fire_no_race_condition) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + uint32_t delay_ms = 5; + timer_->SchedulePeriodic(message_loop_thread.GetWeakPtr(), FROM_HERE, + base::DoNothing(), + base::TimeDelta::FromMilliseconds(delay_ms)); + std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); + timer_->CancelAndWait(); +} + +TEST_F(RepeatingTimerTest, cancel_periodic_task) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + uint32_t delay_ms = 5; + int num_tasks = 5; + auto future = promise_->get_future(); + + timer_->SchedulePeriodic( + message_loop_thread.GetWeakPtr(), FROM_HERE, + base::BindRepeating(&RepeatingTimerTest::IncreaseTaskCounter, + base::Unretained(this), num_tasks, promise_), + base::TimeDelta::FromMilliseconds(delay_ms)); + future.wait(); + timer_->CancelAndWait(); + std::this_thread::sleep_for( + std::chrono::milliseconds(delay_ms + delay_error_ms)); + int counter = counter_; + std::this_thread::sleep_for( + std::chrono::milliseconds(delay_ms + delay_error_ms)); + ASSERT_EQ(counter, counter_); +} + +// Schedule 10 short periodic tasks with interval 1 ms between each; verify the +// functionality +TEST_F(RepeatingTimerTest, schedule_multiple_delayed_tasks) { + VerifyMultipleDelayedTasks(10, 0, 1); +} + +// Schedule 10 periodic tasks with interval 2 ms between each and each takes 1 +// ms; verify the functionality +TEST_F(RepeatingTimerTest, schedule_multiple_delayed_slow_tasks) { + VerifyMultipleDelayedTasks(10, 1, 2); +} + +TEST_F(RepeatingTimerTest, + message_loop_thread_down_cancel_scheduled_periodic_task) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + std::string my_name; + auto future = promise_->get_future(); + uint32_t delay_ms = 5; + int num_tasks = 5; + + timer_->SchedulePeriodic( + message_loop_thread.GetWeakPtr(), FROM_HERE, + base::BindRepeating(&RepeatingTimerTest::IncreaseTaskCounter, + base::Unretained(this), num_tasks, promise_), + base::TimeDelta::FromMilliseconds(delay_ms)); + future.wait(); + message_loop_thread.ShutDown(); + std::this_thread::sleep_for( + std::chrono::milliseconds(delay_ms + delay_error_ms)); + int counter = counter_; + std::this_thread::sleep_for( + std::chrono::milliseconds(delay_ms + delay_error_ms)); + ASSERT_EQ(counter, counter_); +} diff --git a/system/common/timer_unittest.cc b/system/common/timer_unittest.cc deleted file mode 100644 index 1c676cc0b3..0000000000 --- a/system/common/timer_unittest.cc +++ /dev/null @@ -1,401 +0,0 @@ -/* - * 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. - */ - -#include <base/bind.h> -#include <base/logging.h> -#include <gtest/gtest.h> -#include <future> - -#include "message_loop_thread.h" -#include "timer.h" - -using bluetooth::common::MessageLoopThread; -using bluetooth::common::Timer; - -// Allowed error between the expected and actual delay for DoInThreadDelayed(). -constexpr uint32_t delay_error_ms = 3; - -/** - * Unit tests to verify Task Scheduler. - */ -class TimerTest : public ::testing::Test { - public: - void ShouldNotHappen() { FAIL() << "Should not happen"; } - - void IncreaseTaskCounter(int scheduled_tasks, std::promise<void>* promise) { - counter_++; - if (counter_ == scheduled_tasks) { - promise->set_value(); - } - } - - void GetName(std::string* name, std::promise<void>* promise) { - char my_name[256]; - pthread_getname_np(pthread_self(), my_name, sizeof(my_name)); - name->append(my_name); - promise->set_value(); - } - - void SleepAndIncreaseCounter(std::promise<void>* promise, int sleep_ms) { - promise->set_value(); - std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms)); - counter_++; - } - - void VerifyDelayTimeAndSleep(std::chrono::steady_clock::time_point start_time, - int interval_ms, int scheduled_tasks, - int task_length_ms, - std::promise<void>* promise) { - auto end_time = std::chrono::steady_clock::now(); - auto actual_delay = std::chrono::duration_cast<std::chrono::milliseconds>( - end_time - start_time); - counter_++; - int64_t scheduled_delay_ms = interval_ms * counter_; - if (counter_ == scheduled_tasks) { - promise->set_value(); - } - ASSERT_NEAR(scheduled_delay_ms, actual_delay.count(), delay_error_ms); - std::this_thread::sleep_for(std::chrono::milliseconds(task_length_ms)); - } - - void VerifyMultipleDelayedTasks(int scheduled_tasks, int task_length_ms, - int interval_between_tasks_ms) { - std::string name = "test_thread"; - MessageLoopThread message_loop_thread(name); - message_loop_thread.StartUp(); - message_loop_thread.EnableRealTimeScheduling(); - auto future = promise_->get_future(); - auto start_time = std::chrono::steady_clock::now(); - timer_->SchedulePeriodic( - message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind(&TimerTest::VerifyDelayTimeAndSleep, base::Unretained(this), - start_time, interval_between_tasks_ms, scheduled_tasks, - task_length_ms, promise_), - base::TimeDelta::FromMilliseconds(interval_between_tasks_ms)); - future.get(); - timer_->CancelAndWait(); - } - - void CancelTimerAndWait() { timer_->CancelAndWait(); } - - protected: - void SetUp() override { - ::testing::Test::SetUp(); - counter_ = 0; - timer_ = new Timer(); - promise_ = new std::promise<void>(); - } - - void TearDown() override { - if (promise_ != nullptr) { - delete promise_; - promise_ = nullptr; - } - if (timer_ != nullptr) { - delete timer_; - timer_ = nullptr; - } - } - - int counter_; - Timer* timer_; - std::promise<void>* promise_; -}; - -TEST_F(TimerTest, initial_is_not_scheduled) { - ASSERT_FALSE(timer_->IsScheduled()); -} - -TEST_F(TimerTest, schedule_task) { - std::string name = "test_thread"; - MessageLoopThread message_loop_thread(name); - message_loop_thread.StartUp(); - auto future = promise_->get_future(); - std::string my_name; - uint32_t delay_ms = 5; - - timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind(&TimerTest::GetName, base::Unretained(this), - &my_name, promise_), - base::TimeDelta::FromMilliseconds(delay_ms)); - EXPECT_TRUE(timer_->IsScheduled()); - future.get(); - ASSERT_EQ(name, my_name); - std::this_thread::sleep_for(std::chrono::milliseconds(delay_error_ms)); - EXPECT_FALSE(timer_->IsScheduled()); -} - -TEST_F(TimerTest, cancel_without_scheduling) { - std::string name = "test_thread"; - MessageLoopThread message_loop_thread(name); - message_loop_thread.StartUp(); - - EXPECT_FALSE(timer_->IsScheduled()); - timer_->CancelAndWait(); - EXPECT_FALSE(timer_->IsScheduled()); -} - -TEST_F(TimerTest, cancel_in_callback_no_deadlock) { - std::string name = "test_thread"; - MessageLoopThread message_loop_thread(name); - message_loop_thread.StartUp(); - uint32_t delay_ms = 5; - - timer_->Schedule( - message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind(&TimerTest::CancelTimerAndWait, base::Unretained(this)), - base::TimeDelta::FromMilliseconds(delay_ms)); -} - -TEST_F(TimerTest, periodic_run) { - std::string name = "test_thread"; - MessageLoopThread message_loop_thread(name); - message_loop_thread.StartUp(); - auto future = promise_->get_future(); - uint32_t delay_ms = 5; - int num_tasks = 200; - - timer_->SchedulePeriodic( - message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind(&TimerTest::IncreaseTaskCounter, base::Unretained(this), - num_tasks, promise_), - base::TimeDelta::FromMilliseconds(delay_ms)); - future.get(); - ASSERT_GE(counter_, num_tasks); - timer_->CancelAndWait(); -} - -TEST_F(TimerTest, schedule_periodic_task_zero_interval) { - std::string name = "test_thread"; - MessageLoopThread message_loop_thread(name); - message_loop_thread.StartUp(); - uint32_t interval_ms = 0; - - ASSERT_FALSE(timer_->SchedulePeriodic( - message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind(&TimerTest::ShouldNotHappen, base::Unretained(this)), - base::TimeDelta::FromMilliseconds(interval_ms))); - std::this_thread::sleep_for(std::chrono::milliseconds(delay_error_ms)); -} - -// Verify that deleting the timer without cancelling it will cancel the task -TEST_F(TimerTest, periodic_delete_without_cancel) { - std::string name = "test_thread"; - MessageLoopThread message_loop_thread(name); - message_loop_thread.StartUp(); - uint32_t delay_ms = 5; - timer_->SchedulePeriodic( - message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind(&TimerTest::ShouldNotHappen, base::Unretained(this)), - base::TimeDelta::FromMilliseconds(delay_ms)); - delete timer_; - timer_ = nullptr; - std::this_thread::sleep_for(std::chrono::milliseconds(delay_error_ms)); -} - -TEST_F(TimerTest, cancel_single_task) { - std::string name = "test_thread"; - MessageLoopThread message_loop_thread(name); - message_loop_thread.StartUp(); - uint32_t delay_ms = 100000000; - timer_->Schedule( - message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind(&TimerTest::ShouldNotHappen, base::Unretained(this)), - base::TimeDelta::FromMilliseconds(delay_ms)); - std::this_thread::sleep_for(std::chrono::milliseconds(delay_error_ms)); - timer_->CancelAndWait(); -} - -TEST_F(TimerTest, message_loop_thread_down_cancel_task) { - std::string name = "test_thread"; - { - MessageLoopThread message_loop_thread(name); - message_loop_thread.StartUp(); - uint32_t delay_ms = 100000000; - timer_->Schedule( - message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind(&TimerTest::ShouldNotHappen, base::Unretained(this)), - base::TimeDelta::FromMilliseconds(delay_ms)); - std::this_thread::sleep_for(std::chrono::milliseconds(delay_error_ms)); - } -} - -TEST_F(TimerTest, cancel_single_task_near_fire_no_race_condition) { - std::string name = "test_thread"; - MessageLoopThread message_loop_thread(name); - message_loop_thread.StartUp(); - uint32_t delay_ms = 5; - timer_->SchedulePeriodic(message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind([]() {}), - base::TimeDelta::FromMilliseconds(delay_ms)); - std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); - timer_->CancelAndWait(); -} - -TEST_F(TimerTest, cancel_periodic_task) { - std::string name = "test_thread"; - MessageLoopThread message_loop_thread(name); - message_loop_thread.StartUp(); - uint32_t delay_ms = 5; - int num_tasks = 5; - auto future = promise_->get_future(); - - timer_->SchedulePeriodic( - message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind(&TimerTest::IncreaseTaskCounter, base::Unretained(this), - num_tasks, promise_), - base::TimeDelta::FromMilliseconds(delay_ms)); - future.wait(); - timer_->CancelAndWait(); - std::this_thread::sleep_for( - std::chrono::milliseconds(delay_ms + delay_error_ms)); - int counter = counter_; - std::this_thread::sleep_for( - std::chrono::milliseconds(delay_ms + delay_error_ms)); - ASSERT_EQ(counter, counter_); -} - -// Verify that if a task is being executed, then cancelling it is no-op -TEST_F(TimerTest, cancel_current_task_no_effect) { - std::string name = "test_thread"; - MessageLoopThread message_loop_thread(name); - message_loop_thread.StartUp(); - auto future = promise_->get_future(); - uint32_t delay_ms = 5; - - timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind(&TimerTest::SleepAndIncreaseCounter, - base::Unretained(this), promise_, delay_ms), - base::TimeDelta::FromMilliseconds(delay_ms)); - EXPECT_TRUE(timer_->IsScheduled()); - future.get(); - timer_->CancelAndWait(); - ASSERT_EQ(counter_, 1); - EXPECT_FALSE(timer_->IsScheduled()); -} - -// Schedule 10 short periodic tasks with interval 1 ms between each; verify the -// functionality -TEST_F(TimerTest, schedule_multiple_delayed_tasks) { - VerifyMultipleDelayedTasks(10, 0, 1); -} - -// Schedule 10 periodic tasks with interval 2 ms between each and each takes 1 -// ms; verify the functionality -TEST_F(TimerTest, schedule_multiple_delayed_slow_tasks) { - VerifyMultipleDelayedTasks(10, 1, 2); -} - -TEST_F(TimerTest, message_loop_thread_down_cancel_scheduled_periodic_task) { - std::string name = "test_thread"; - MessageLoopThread message_loop_thread(name); - message_loop_thread.StartUp(); - std::string my_name; - auto future = promise_->get_future(); - uint32_t delay_ms = 5; - int num_tasks = 5; - - timer_->SchedulePeriodic( - message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind(&TimerTest::IncreaseTaskCounter, base::Unretained(this), - num_tasks, promise_), - base::TimeDelta::FromMilliseconds(delay_ms)); - future.wait(); - message_loop_thread.ShutDown(); - std::this_thread::sleep_for( - std::chrono::milliseconds(delay_ms + delay_error_ms)); - int counter = counter_; - std::this_thread::sleep_for( - std::chrono::milliseconds(delay_ms + delay_error_ms)); - ASSERT_EQ(counter, counter_); -} - -TEST_F(TimerTest, schedule_task_cancel_previous_task) { - std::string name = "test_thread"; - MessageLoopThread message_loop_thread(name); - message_loop_thread.StartUp(); - std::string my_name; - auto future = promise_->get_future(); - uint32_t delay_ms = 5; - int num_tasks = 5; - - timer_->SchedulePeriodic( - message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind(&TimerTest::IncreaseTaskCounter, base::Unretained(this), - num_tasks, promise_), - base::TimeDelta::FromMilliseconds(delay_ms)); - future.wait(); - timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind([]() {}), - base::TimeDelta::FromMilliseconds(delay_ms)); - std::this_thread::sleep_for( - std::chrono::milliseconds(delay_ms + delay_error_ms)); - int counter = counter_; - std::this_thread::sleep_for( - std::chrono::milliseconds(delay_ms + delay_error_ms)); - ASSERT_EQ(counter, counter_); -} - -TEST_F(TimerTest, reschedule_task_dont_invoke_new_task_early) { - std::string name = "test_thread"; - MessageLoopThread message_loop_thread(name); - message_loop_thread.StartUp(); - uint32_t delay_ms = 5; - timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind([]() {}), - base::TimeDelta::FromMilliseconds(delay_ms)); - std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms - 2)); - timer_->Schedule( - message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind(&TimerTest::ShouldNotHappen, base::Unretained(this)), - base::TimeDelta::FromMilliseconds(delay_ms + 1000)); - std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); -} - -TEST_F(TimerTest, reschedule_task_when_firing_dont_invoke_new_task_early) { - std::string name = "test_thread"; - MessageLoopThread message_loop_thread(name); - message_loop_thread.StartUp(); - uint32_t delay_ms = 5; - timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind([]() {}), - base::TimeDelta::FromMilliseconds(delay_ms)); - std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); - timer_->Schedule( - message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind(&TimerTest::ShouldNotHappen, base::Unretained(this)), - base::TimeDelta::FromMilliseconds(delay_ms + 1000)); - std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); -} - -TEST_F(TimerTest, reschedule_task_when_firing_must_schedule_new_task) { - std::string name = "test_thread"; - MessageLoopThread message_loop_thread(name); - message_loop_thread.StartUp(); - uint32_t delay_ms = 5; - std::string my_name; - auto future = promise_->get_future(); - timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind([]() {}), - base::TimeDelta::FromMilliseconds(delay_ms)); - std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); - timer_->Schedule(message_loop_thread.GetWeakPtr(), FROM_HERE, - base::Bind(&TimerTest::GetName, base::Unretained(this), - &my_name, promise_), - base::TimeDelta::FromMilliseconds(delay_ms)); - future.get(); - ASSERT_EQ(name, my_name); -} |