OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "content/renderer/devtools/devtools_cpu_throttler.h" |
| 6 |
| 7 #if defined(OS_POSIX) |
| 8 #include <signal.h> |
| 9 #define USE_SIGNALS |
| 10 #endif |
| 11 |
| 12 #include "base/atomicops.h" |
| 13 #include "base/synchronization/cancellation_flag.h" |
| 14 #include "base/threading/platform_thread.h" |
| 15 |
| 16 using base::subtle::Atomic32; |
| 17 using base::subtle::Acquire_Load; |
| 18 using base::subtle::Release_Store; |
| 19 |
| 20 namespace content { |
| 21 |
| 22 class CPUThrottlingThread final : public base::PlatformThread::Delegate { |
| 23 public: |
| 24 explicit CPUThrottlingThread(double rate); |
| 25 ~CPUThrottlingThread() override; |
| 26 |
| 27 void SetThrottlingRate(double rate); |
| 28 |
| 29 private: |
| 30 void ThreadMain() override; |
| 31 |
| 32 void Start(); |
| 33 void Stop(); |
| 34 void Throttle(); |
| 35 |
| 36 static void SuspendThread(base::PlatformThreadHandle thread_handle); |
| 37 static void ResumeThread(base::PlatformThreadHandle thread_handle); |
| 38 |
| 39 #ifdef USE_SIGNALS |
| 40 void InstallSignalHandler(); |
| 41 void RestoreSignalHandler(); |
| 42 static void HandleSignal(int signal); |
| 43 |
| 44 static bool signal_handler_installed_; |
| 45 static struct sigaction old_signal_handler_; |
| 46 static Atomic32 suspended_; |
| 47 #endif |
| 48 static Atomic32 thread_exists_; |
| 49 |
| 50 base::PlatformThreadHandle throttled_thread_handle_; |
| 51 base::PlatformThreadHandle throttling_thread_handle_; |
| 52 base::CancellationFlag cancellation_flag_; |
| 53 Atomic32 throttling_rate_percent_; |
| 54 |
| 55 DISALLOW_COPY_AND_ASSIGN(CPUThrottlingThread); |
| 56 }; |
| 57 |
| 58 #ifdef USE_SIGNALS |
| 59 bool CPUThrottlingThread::signal_handler_installed_; |
| 60 struct sigaction CPUThrottlingThread::old_signal_handler_; |
| 61 Atomic32 CPUThrottlingThread::suspended_; |
| 62 #endif |
| 63 Atomic32 CPUThrottlingThread::thread_exists_; |
| 64 |
| 65 CPUThrottlingThread::CPUThrottlingThread(double rate) |
| 66 : throttled_thread_handle_(base::PlatformThread::CurrentHandle()), |
| 67 throttling_rate_percent_(static_cast<Atomic32>(rate * 100)) { |
| 68 CHECK(base::subtle::NoBarrier_AtomicExchange(&thread_exists_, 1) == 0); |
| 69 Start(); |
| 70 } |
| 71 |
| 72 CPUThrottlingThread::~CPUThrottlingThread() { |
| 73 Stop(); |
| 74 CHECK(base::subtle::NoBarrier_AtomicExchange(&thread_exists_, 0) == 1); |
| 75 } |
| 76 |
| 77 void CPUThrottlingThread::SetThrottlingRate(double rate) { |
| 78 Release_Store(&throttling_rate_percent_, static_cast<Atomic32>(rate * 100)); |
| 79 } |
| 80 |
| 81 void CPUThrottlingThread::ThreadMain() { |
| 82 base::PlatformThread::SetName("DevToolsCPUThrottlingThread"); |
| 83 while (!cancellation_flag_.IsSet()) { |
| 84 Throttle(); |
| 85 } |
| 86 } |
| 87 |
| 88 #ifdef USE_SIGNALS |
| 89 |
| 90 // static |
| 91 void CPUThrottlingThread::InstallSignalHandler() { |
| 92 // There must be the only one! |
| 93 DCHECK(!signal_handler_installed_); |
| 94 struct sigaction sa; |
| 95 sa.sa_handler = &HandleSignal; |
| 96 sigemptyset(&sa.sa_mask); |
| 97 sa.sa_flags = SA_RESTART; |
| 98 signal_handler_installed_ = |
| 99 (sigaction(SIGUSR2, &sa, &old_signal_handler_) == 0); |
| 100 } |
| 101 |
| 102 // static |
| 103 void CPUThrottlingThread::RestoreSignalHandler() { |
| 104 if (!signal_handler_installed_) |
| 105 return; |
| 106 sigaction(SIGUSR2, &old_signal_handler_, 0); |
| 107 signal_handler_installed_ = false; |
| 108 } |
| 109 |
| 110 // static |
| 111 void CPUThrottlingThread::HandleSignal(int signal) { |
| 112 if (signal != SIGUSR2) |
| 113 return; |
| 114 while (Acquire_Load(&suspended_)) { |
| 115 } |
| 116 } |
| 117 |
| 118 #endif // USE_SIGNALS |
| 119 |
| 120 // static |
| 121 void CPUThrottlingThread::SuspendThread( |
| 122 base::PlatformThreadHandle thread_handle) { |
| 123 #ifdef USE_SIGNALS |
| 124 Release_Store(&suspended_, 1); |
| 125 pthread_kill(thread_handle.platform_handle(), SIGUSR2); |
| 126 #endif |
| 127 } |
| 128 |
| 129 // static |
| 130 void CPUThrottlingThread::ResumeThread( |
| 131 base::PlatformThreadHandle thread_handle) { |
| 132 #ifdef USE_SIGNALS |
| 133 Release_Store(&suspended_, 0); |
| 134 #endif |
| 135 } |
| 136 |
| 137 void CPUThrottlingThread::Start() { |
| 138 #ifdef USE_SIGNALS |
| 139 InstallSignalHandler(); |
| 140 #endif |
| 141 if (!base::PlatformThread::Create(0, this, &throttling_thread_handle_)) { |
| 142 LOG(ERROR) << "Failed to create throttling thread."; |
| 143 } |
| 144 } |
| 145 |
| 146 void CPUThrottlingThread::Stop() { |
| 147 cancellation_flag_.Set(); |
| 148 base::PlatformThread::Join(throttling_thread_handle_); |
| 149 #ifdef USE_SIGNALS |
| 150 RestoreSignalHandler(); |
| 151 #endif |
| 152 } |
| 153 |
| 154 void CPUThrottlingThread::Throttle() { |
| 155 const int quant_time_us = 200; |
| 156 double rate = Acquire_Load(&throttling_rate_percent_) / 100.; |
| 157 base::TimeDelta run_duration = |
| 158 base::TimeDelta::FromMicroseconds(static_cast<int>(quant_time_us / rate)); |
| 159 base::TimeDelta sleep_duration = |
| 160 base::TimeDelta::FromMicroseconds(quant_time_us) - run_duration; |
| 161 base::PlatformThread::Sleep(run_duration); |
| 162 SuspendThread(throttled_thread_handle_); |
| 163 base::PlatformThread::Sleep(sleep_duration); |
| 164 ResumeThread(throttled_thread_handle_); |
| 165 } |
| 166 |
| 167 DevToolsCPUThrottler::DevToolsCPUThrottler() {} |
| 168 |
| 169 DevToolsCPUThrottler::~DevToolsCPUThrottler() {} |
| 170 |
| 171 void DevToolsCPUThrottler::SetThrottlingRate(double rate) { |
| 172 if (rate <= 1) { |
| 173 if (throttling_thread_) { |
| 174 throttling_thread_.reset(); |
| 175 } |
| 176 return; |
| 177 } |
| 178 if (throttling_thread_) { |
| 179 throttling_thread_->SetThrottlingRate(rate); |
| 180 } else { |
| 181 throttling_thread_.reset(new CPUThrottlingThread(rate)); |
| 182 } |
| 183 } |
| 184 |
| 185 } // namespace content |
OLD | NEW |