diff options
author | Hansong Zhang <hsz@google.com> | 2018-08-14 14:29:23 -0700 |
---|---|---|
committer | Hansong Zhang <hsz@google.com> | 2018-09-13 13:37:42 -0700 |
commit | f05a8c49a889b5a83841e4bf55dd3cf5a1afa8d0 (patch) | |
tree | 5bc7d1343c049f61e0bafec0158a3f336dc2b137 /system/common/timer.cc | |
parent | 76250727b2fa2a5a6364edcb20e4d379c82090a4 (diff) |
Add Timer as an alternative to osi alarm
* Add a private method MessageLoopThread.DoInThreadDelayed to post a
delayed task in message loop, as an alternative approach to osi alarm
clock
* Add a unit test for MessageLoopThread to check ShutDown() waits until
current task finishes
* Add Timer using MessageLoopThread.DoInThreadDelayed
* Timer provides similar API as osi alarm, and uses same OS clock (boot
timer) as alarm
* Add benchmark and unit tests to ensure the performance is comparable
to the existing osi alarm
Test: Run unit test and benchmark test
./test/run_unit_tests.sh bluetooth_test_common
./test/run_benchmarks.sh bluetooth_benchmark_timer_performance
--benchmark_repetitions=10 --benchmark_report_aggregates_only=true
Bug: 110303473
Change-Id: I6f2e7ae2f80f9889fc5fe3c8cd6b9b2670938b46
Diffstat (limited to 'system/common/timer.cc')
-rw-r--r-- | system/common/timer.cc | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/system/common/timer.cc b/system/common/timer.cc new file mode 100644 index 0000000000..b4d126c7d8 --- /dev/null +++ b/system/common/timer.cc @@ -0,0 +1,174 @@ +/* + * 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 "timer.h" +#include "message_loop_thread.h" +#include "time_util.h" + +namespace bluetooth { + +namespace common { + +constexpr base::TimeDelta kMinimumPeriod = base::TimeDelta::FromMicroseconds(1); + +Timer::~Timer() { + 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 Timer::Schedule(const base::WeakPtr<MessageLoopThread>& thread, + const tracked_objects::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 tracked_objects::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 tracked_objects::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(); + std::lock_guard<std::recursive_mutex> api_lock(api_mutex_); + if (thread == nullptr) { + LOG(ERROR) << __func__ << ": thread must be non-null"; + return false; + } + CancelAndWait(); + expected_time_next_task_us_ = time_next_task_us; + task_ = std::move(task); + uint64_t time_until_next_us = time_next_task_us - time_get_os_boottime_us(); + if (!thread->DoInThreadDelayed( + from_here, task_wrapper_, + base::TimeDelta::FromMicroseconds(time_until_next_us))) { + LOG(ERROR) << __func__ + << ": failed to post task to message loop for thread " << *thread + << ", from " << from_here.ToString(); + expected_time_next_task_us_ = 0; + task_.Reset(); + return false; + } + message_loop_thread_ = thread; + period_ = delay; + is_periodic_ = is_periodic; + return true; +} + +// This runs on user thread +void Timer::Cancel() { + std::lock_guard<std::recursive_mutex> api_lock(api_mutex_); + CancelHelper(false); +} + +// This runs on user thread +void Timer::CancelAndWait() { + std::lock_guard<std::recursive_mutex> api_lock(api_mutex_); + CancelHelper(true); +} + +// This runs on user thread +void Timer::CancelHelper(bool is_synchronous) { + if (message_loop_thread_ == nullptr) { + return; + } + std::promise<void> promise; + auto future = promise.get_future(); + if (message_loop_thread_->GetThreadId() == + base::PlatformThread::CurrentId()) { + CancelClosure(std::move(promise)); + return; + } + message_loop_thread_->DoInThread( + FROM_HERE, base::BindOnce(&Timer::CancelClosure, base::Unretained(this), + std::move(promise))); + if (is_synchronous) { + future.wait(); + } +} + +// This runs on message loop thread +void Timer::CancelClosure(std::promise<void> promise) { + message_loop_thread_ = nullptr; + task_.Reset(); + period_ = base::TimeDelta(); + is_periodic_ = false; + expected_time_next_task_us_ = 0; + promise.set_value(); +} + +// This runs in user thread +bool Timer::IsScheduled() const { + std::lock_guard<std::recursive_mutex> api_lock(api_mutex_); + return message_loop_thread_ != nullptr && message_loop_thread_->IsRunning(); +} + +// This runs in message loop thread +void Timer::RunTask() { + if (message_loop_thread_ == nullptr || !message_loop_thread_->IsRunning()) { + LOG(ERROR) << __func__ + << ": message_loop_thread_ is null or is not running"; + return; + } + if (is_periodic_) { + int64_t period_us = period_.InMicroseconds(); + expected_time_next_task_us_ += period_us; + uint64_t time_now_us = time_get_os_boottime_us(); + int64_t remaining_time_us = expected_time_next_task_us_ - time_now_us; + if (remaining_time_us < 0) { + // if remaining_time_us is negative, schedule the task to the nearest + // multiple of period + remaining_time_us = + (remaining_time_us % period_us + period_us) % period_us; + } + message_loop_thread_->DoInThreadDelayed( + FROM_HERE, task_wrapper_, + base::TimeDelta::FromMicroseconds(remaining_time_us)); + } + uint64_t time_before_task_us = time_get_os_boottime_us(); + task_.Run(); + uint64_t time_after_task_us = time_get_os_boottime_us(); + int64_t task_time_us = + static_cast<int64_t>(time_after_task_us - time_before_task_us); + if (is_periodic_ && task_time_us > period_.InMicroseconds()) { + LOG(ERROR) << __func__ << ": Periodic task execution took " << task_time_us + << " microseconds, longer than interval " + << period_.InMicroseconds() << " microseconds"; + } + if (!is_periodic_) { + message_loop_thread_ = nullptr; + task_.Reset(); + period_ = base::TimeDelta(); + expected_time_next_task_us_ = 0; + } +} + +} // namespace common + +} // namespace bluetooth |