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 CPUThrottlingThread(double rate); | |
pfeldman
2015/12/12 00:33:18
explicit
| |
25 ~CPUThrottlingThread() override; | |
26 | |
27 void SetThrottlingRate(double rate); | |
28 | |
29 void ThreadMain() override; | |
30 | |
31 private: | |
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 |