Chromium Code Reviews| Index: content/renderer/devtools/devtools_cpu_throttler.cc |
| diff --git a/content/renderer/devtools/devtools_cpu_throttler.cc b/content/renderer/devtools/devtools_cpu_throttler.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..8229de611e84be6d557acf7ef9c51975930a58da |
| --- /dev/null |
| +++ b/content/renderer/devtools/devtools_cpu_throttler.cc |
| @@ -0,0 +1,185 @@ |
| +// Copyright 2015 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "content/renderer/devtools/devtools_cpu_throttler.h" |
| + |
| +#if defined(OS_POSIX) |
| +#include <signal.h> |
| +#define USE_SIGNALS |
| +#endif |
| + |
| +#include "base/atomicops.h" |
| +#include "base/synchronization/cancellation_flag.h" |
| +#include "base/threading/platform_thread.h" |
| + |
| +using base::subtle::Atomic32; |
| +using base::subtle::Acquire_Load; |
| +using base::subtle::Release_Store; |
| + |
| +namespace content { |
| + |
| +class CPUThrottlingThread final : public base::PlatformThread::Delegate { |
| + public: |
| + CPUThrottlingThread(double rate); |
|
pfeldman
2015/12/12 00:33:18
explicit
|
| + ~CPUThrottlingThread() override; |
| + |
| + void SetThrottlingRate(double rate); |
| + |
| + void ThreadMain() override; |
| + |
| + private: |
| + void Start(); |
| + void Stop(); |
| + void Throttle(); |
| + |
| + static void SuspendThread(base::PlatformThreadHandle thread_handle); |
| + static void ResumeThread(base::PlatformThreadHandle thread_handle); |
| + |
| +#ifdef USE_SIGNALS |
| + void InstallSignalHandler(); |
| + void RestoreSignalHandler(); |
| + static void HandleSignal(int signal); |
| + |
| + static bool signal_handler_installed_; |
| + static struct sigaction old_signal_handler_; |
| + static Atomic32 suspended_; |
| +#endif |
| + static Atomic32 thread_exists_; |
| + |
| + base::PlatformThreadHandle throttled_thread_handle_; |
| + base::PlatformThreadHandle throttling_thread_handle_; |
| + base::CancellationFlag cancellation_flag_; |
| + Atomic32 throttling_rate_percent_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(CPUThrottlingThread); |
| +}; |
| + |
| +#ifdef USE_SIGNALS |
| +bool CPUThrottlingThread::signal_handler_installed_; |
| +struct sigaction CPUThrottlingThread::old_signal_handler_; |
| +Atomic32 CPUThrottlingThread::suspended_; |
| +#endif |
| +Atomic32 CPUThrottlingThread::thread_exists_; |
| + |
| +CPUThrottlingThread::CPUThrottlingThread(double rate) |
| + : throttled_thread_handle_(base::PlatformThread::CurrentHandle()), |
| + throttling_rate_percent_(static_cast<Atomic32>(rate * 100)) { |
| + CHECK(base::subtle::NoBarrier_AtomicExchange(&thread_exists_, 1) == 0); |
| + Start(); |
| +} |
| + |
| +CPUThrottlingThread::~CPUThrottlingThread() { |
| + Stop(); |
| + CHECK(base::subtle::NoBarrier_AtomicExchange(&thread_exists_, 0) == 1); |
| +} |
| + |
| +void CPUThrottlingThread::SetThrottlingRate(double rate) { |
| + Release_Store(&throttling_rate_percent_, static_cast<Atomic32>(rate * 100)); |
| +} |
| + |
| +void CPUThrottlingThread::ThreadMain() { |
| + base::PlatformThread::SetName("DevToolsCPUThrottlingThread"); |
| + while (!cancellation_flag_.IsSet()) { |
| + Throttle(); |
| + } |
| +} |
| + |
| +#ifdef USE_SIGNALS |
| + |
| +// static |
| +void CPUThrottlingThread::InstallSignalHandler() { |
| + // There must be the only one! |
| + DCHECK(!signal_handler_installed_); |
| + struct sigaction sa; |
| + sa.sa_handler = &HandleSignal; |
| + sigemptyset(&sa.sa_mask); |
| + sa.sa_flags = SA_RESTART; |
| + signal_handler_installed_ = |
| + (sigaction(SIGUSR2, &sa, &old_signal_handler_) == 0); |
| +} |
| + |
| +// static |
| +void CPUThrottlingThread::RestoreSignalHandler() { |
| + if (!signal_handler_installed_) |
| + return; |
| + sigaction(SIGUSR2, &old_signal_handler_, 0); |
| + signal_handler_installed_ = false; |
| +} |
| + |
| +// static |
| +void CPUThrottlingThread::HandleSignal(int signal) { |
| + if (signal != SIGUSR2) |
| + return; |
| + while (Acquire_Load(&suspended_)) { |
| + } |
| +} |
| + |
| +#endif // USE_SIGNALS |
| + |
| +// static |
| +void CPUThrottlingThread::SuspendThread( |
| + base::PlatformThreadHandle thread_handle) { |
| +#ifdef USE_SIGNALS |
| + Release_Store(&suspended_, 1); |
| + pthread_kill(thread_handle.platform_handle(), SIGUSR2); |
| +#endif |
| +} |
| + |
| +// static |
| +void CPUThrottlingThread::ResumeThread( |
| + base::PlatformThreadHandle thread_handle) { |
| +#ifdef USE_SIGNALS |
| + Release_Store(&suspended_, 0); |
| +#endif |
| +} |
| + |
| +void CPUThrottlingThread::Start() { |
| +#ifdef USE_SIGNALS |
| + InstallSignalHandler(); |
| +#endif |
| + if (!base::PlatformThread::Create(0, this, &throttling_thread_handle_)) { |
| + LOG(ERROR) << "Failed to create throttling thread."; |
| + } |
| +} |
| + |
| +void CPUThrottlingThread::Stop() { |
| + cancellation_flag_.Set(); |
| + base::PlatformThread::Join(throttling_thread_handle_); |
| +#ifdef USE_SIGNALS |
| + RestoreSignalHandler(); |
| +#endif |
| +} |
| + |
| +void CPUThrottlingThread::Throttle() { |
| + const int quant_time_us = 200; |
| + double rate = Acquire_Load(&throttling_rate_percent_) / 100.; |
| + base::TimeDelta run_duration = |
| + base::TimeDelta::FromMicroseconds(static_cast<int>(quant_time_us / rate)); |
| + base::TimeDelta sleep_duration = |
| + base::TimeDelta::FromMicroseconds(quant_time_us) - run_duration; |
| + base::PlatformThread::Sleep(run_duration); |
| + SuspendThread(throttled_thread_handle_); |
| + base::PlatformThread::Sleep(sleep_duration); |
| + ResumeThread(throttled_thread_handle_); |
| +} |
| + |
| +DevToolsCPUThrottler::DevToolsCPUThrottler() {} |
| + |
| +DevToolsCPUThrottler::~DevToolsCPUThrottler() {} |
| + |
| +void DevToolsCPUThrottler::SetThrottlingRate(double rate) { |
| + if (rate <= 1) { |
| + if (throttling_thread_) { |
| + throttling_thread_.reset(); |
| + } |
| + return; |
| + } |
| + if (throttling_thread_) { |
| + throttling_thread_->SetThrottlingRate(rate); |
| + } else { |
| + throttling_thread_.reset(new CPUThrottlingThread(rate)); |
| + } |
| +} |
| + |
| +} // namespace content |