| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/browser_watcher/window_hang_monitor_win.h" | 5 #include "components/browser_watcher/window_hang_monitor_win.h" |
| 6 | 6 |
| 7 #include <vector> | 7 #include <vector> |
| 8 | 8 |
| 9 #include "base/base_switches.h" | 9 #include "base/base_switches.h" |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 48 HangMonitorThread() | 48 HangMonitorThread() |
| 49 : event_(WindowHangMonitor::WINDOW_NOT_FOUND), | 49 : event_(WindowHangMonitor::WINDOW_NOT_FOUND), |
| 50 event_received_(false, false), | 50 event_received_(false, false), |
| 51 thread_("HangMonitorThread") {} | 51 thread_("HangMonitorThread") {} |
| 52 | 52 |
| 53 ~HangMonitorThread() { | 53 ~HangMonitorThread() { |
| 54 if (hang_monitor_.get()) | 54 if (hang_monitor_.get()) |
| 55 DestroyWatcher(); | 55 DestroyWatcher(); |
| 56 } | 56 } |
| 57 | 57 |
| 58 // Starts the background thread and the monitor to observe the window named | 58 // Starts the background thread and the monitor to observe Chrome message |
| 59 // |window_name| in |process|. Blocks until the monitor has been initialized. | 59 // window for |process|. Blocks until the monitor has been initialized. |
| 60 bool Start(base::Process process, const base::string16& window_name) { | 60 bool Start(base::Process process) { |
| 61 if (!thread_.StartWithOptions( | 61 if (!thread_.StartWithOptions( |
| 62 base::Thread::Options(base::MessageLoop::TYPE_UI, 0))) { | 62 base::Thread::Options(base::MessageLoop::TYPE_UI, 0))) { |
| 63 return false; | 63 return false; |
| 64 } | 64 } |
| 65 | 65 |
| 66 base::WaitableEvent complete(false, false); | 66 base::WaitableEvent complete(false, false); |
| 67 if (!thread_.task_runner()->PostTask( | 67 if (!thread_.task_runner()->PostTask( |
| 68 FROM_HERE, base::Bind(&HangMonitorThread::StartupOnThread, | 68 FROM_HERE, |
| 69 base::Unretained(this), window_name, | 69 base::Bind(&HangMonitorThread::StartupOnThread, |
| 70 base::Passed(process.Pass()), | 70 base::Unretained(this), base::Passed(process.Pass()), |
| 71 base::Unretained(&complete)))) { | 71 base::Unretained(&complete)))) { |
| 72 return false; | 72 return false; |
| 73 } | 73 } |
| 74 | 74 |
| 75 complete.Wait(); | 75 complete.Wait(); |
| 76 | 76 |
| 77 return true; | 77 return true; |
| 78 } | 78 } |
| 79 | 79 |
| 80 // Returns true if a window event is detected within |timeout|. | 80 // Returns true if a window event is detected within |timeout|. |
| 81 bool TimedWaitForEvent(base::TimeDelta timeout) { | 81 bool TimedWaitForEvent(base::TimeDelta timeout) { |
| (...skipping 19 matching lines...) Expand all Loading... |
| 101 private: | 101 private: |
| 102 // Invoked when the monitor signals an event. Unblocks a call to | 102 // Invoked when the monitor signals an event. Unblocks a call to |
| 103 // TimedWaitForEvent or WaitForEvent. | 103 // TimedWaitForEvent or WaitForEvent. |
| 104 void EventCallback(WindowHangMonitor::WindowEvent event) { | 104 void EventCallback(WindowHangMonitor::WindowEvent event) { |
| 105 if (event_received_.IsSignaled()) | 105 if (event_received_.IsSignaled()) |
| 106 ADD_FAILURE() << "Multiple calls to EventCallback."; | 106 ADD_FAILURE() << "Multiple calls to EventCallback."; |
| 107 event_ = event; | 107 event_ = event; |
| 108 event_received_.Signal(); | 108 event_received_.Signal(); |
| 109 } | 109 } |
| 110 | 110 |
| 111 // Initializes the WindowHangMonitor to observe the window named |window_name| | 111 // Initializes the WindowHangMonitor to observe the Chrome message window for |
| 112 // in |process|. Signals |complete| when done. | 112 // |process|. Signals |complete| when done. |
| 113 void StartupOnThread(const base::string16& window_name, | 113 void StartupOnThread(base::Process process, base::WaitableEvent* complete) { |
| 114 base::Process process, | |
| 115 base::WaitableEvent* complete) { | |
| 116 hang_monitor_.reset(new WindowHangMonitor( | 114 hang_monitor_.reset(new WindowHangMonitor( |
| 117 base::TimeDelta::FromMilliseconds(100), | 115 base::TimeDelta::FromMilliseconds(100), |
| 118 base::TimeDelta::FromMilliseconds(100), | 116 base::TimeDelta::FromMilliseconds(100), |
| 119 base::Bind(&HangMonitorThread::EventCallback, base::Unretained(this)))); | 117 base::Bind(&HangMonitorThread::EventCallback, base::Unretained(this)))); |
| 120 hang_monitor_->Initialize(process.Pass(), window_name); | 118 hang_monitor_->Initialize(process.Pass()); |
| 121 complete->Signal(); | 119 complete->Signal(); |
| 122 } | 120 } |
| 123 | 121 |
| 124 // Destroys the WindowHangMonitor. | 122 // Destroys the WindowHangMonitor. |
| 125 void ShutdownOnThread() { hang_monitor_.reset(); } | 123 void ShutdownOnThread() { hang_monitor_.reset(); } |
| 126 | 124 |
| 127 // The detected event. Invalid if |event_received_| has not been signaled. | 125 // The detected event. Invalid if |event_received_| has not been signaled. |
| 128 WindowHangMonitor::WindowEvent event_; | 126 WindowHangMonitor::WindowEvent event_; |
| 129 // Indicates that |event_| has been assigned in response to a callback from | 127 // Indicates that |event_| has been assigned in response to a callback from |
| 130 // the WindowHangMonitor. | 128 // the WindowHangMonitor. |
| 131 base::WaitableEvent event_received_; | 129 base::WaitableEvent event_received_; |
| 132 // The WindowHangMonitor under test. | 130 // The WindowHangMonitor under test. |
| 133 scoped_ptr<WindowHangMonitor> hang_monitor_; | 131 scoped_ptr<WindowHangMonitor> hang_monitor_; |
| 134 // The background thread. | 132 // The background thread. |
| 135 base::Thread thread_; | 133 base::Thread thread_; |
| 136 | 134 |
| 137 DISALLOW_COPY_AND_ASSIGN(HangMonitorThread); | 135 DISALLOW_COPY_AND_ASSIGN(HangMonitorThread); |
| 138 }; | 136 }; |
| 139 | 137 |
| 140 class WindowHangMonitorTest : public testing::Test { | 138 class WindowHangMonitorTest : public testing::Test { |
| 141 public: | 139 public: |
| 142 WindowHangMonitorTest() | 140 WindowHangMonitorTest() |
| 143 : ping_event_(false, false), | 141 : ping_event_(false, false), |
| 144 pings_(0), | 142 pings_(0), |
| 145 window_thread_("WindowHangMonitorTest window_thread") {} | 143 window_thread_("WindowHangMonitorTest window_thread") {} |
| 146 | 144 |
| 147 void SetUp() override { | 145 void SetUp() override { |
| 148 // Pick a window name unique to this process. | |
| 149 window_name_ = base::StringPrintf(L"WindowHanMonitorTest-%d", | |
| 150 base::GetCurrentProcId()); | |
| 151 ASSERT_TRUE(window_thread_.StartWithOptions( | 146 ASSERT_TRUE(window_thread_.StartWithOptions( |
| 152 base::Thread::Options(base::MessageLoop::TYPE_UI, 0))); | 147 base::Thread::Options(base::MessageLoop::TYPE_UI, 0))); |
| 153 } | 148 } |
| 154 | 149 |
| 155 void TearDown() override { | 150 void TearDown() override { |
| 156 DeleteMessageWindow(); | 151 DeleteMessageWindow(); |
| 157 window_thread_.Stop(); | 152 window_thread_.Stop(); |
| 158 } | 153 } |
| 159 | 154 |
| 160 void CreateMessageWindow() { | 155 void CreateMessageWindow() { |
| 161 bool succeeded = false; | 156 bool succeeded = false; |
| 162 base::WaitableEvent created(true, false); | 157 base::WaitableEvent created(true, false); |
| 163 ASSERT_TRUE(window_thread_.task_runner()->PostTask( | 158 ASSERT_TRUE(window_thread_.task_runner()->PostTask( |
| 164 FROM_HERE, | 159 FROM_HERE, |
| 165 base::Bind(&WindowHangMonitorTest::CreateMessageWindowInWorkerThread, | 160 base::Bind(&WindowHangMonitorTest::CreateMessageWindowInWorkerThread, |
| 166 base::Unretained(this), window_name_, &succeeded, | 161 base::Unretained(this), &succeeded, &created))); |
| 167 &created))); | |
| 168 created.Wait(); | 162 created.Wait(); |
| 169 ASSERT_TRUE(succeeded); | 163 ASSERT_TRUE(succeeded); |
| 170 } | 164 } |
| 171 | 165 |
| 172 void DeleteMessageWindow() { | 166 void DeleteMessageWindow() { |
| 173 base::WaitableEvent deleted(true, false); | 167 base::WaitableEvent deleted(true, false); |
| 174 window_thread_.task_runner()->PostTask( | 168 window_thread_.task_runner()->PostTask( |
| 175 FROM_HERE, | 169 FROM_HERE, |
| 176 base::Bind(&WindowHangMonitorTest::DeleteMessageWindowInWorkerThread, | 170 base::Bind(&WindowHangMonitorTest::DeleteMessageWindowInWorkerThread, |
| 177 base::Unretained(this), &deleted)); | 171 base::Unretained(this), &deleted)); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 191 ping_event_.Wait(); | 185 ping_event_.Wait(); |
| 192 } | 186 } |
| 193 } | 187 } |
| 194 | 188 |
| 195 HangMonitorThread& monitor_thread() { return monitor_thread_; } | 189 HangMonitorThread& monitor_thread() { return monitor_thread_; } |
| 196 | 190 |
| 197 const base::win::MessageWindow* message_window() const { | 191 const base::win::MessageWindow* message_window() const { |
| 198 return message_window_.get(); | 192 return message_window_.get(); |
| 199 } | 193 } |
| 200 | 194 |
| 201 const base::string16& window_name() const { return window_name_; } | |
| 202 | |
| 203 base::Thread* window_thread() { return &window_thread_; } | 195 base::Thread* window_thread() { return &window_thread_; } |
| 204 | 196 |
| 205 private: | 197 private: |
| 206 bool MessageCallback(UINT message, | 198 bool MessageCallback(UINT message, |
| 207 WPARAM wparam, | 199 WPARAM wparam, |
| 208 LPARAM lparam, | 200 LPARAM lparam, |
| 209 LRESULT* result) { | 201 LRESULT* result) { |
| 210 EXPECT_EQ(window_thread_.message_loop(), base::MessageLoop::current()); | 202 EXPECT_EQ(window_thread_.message_loop(), base::MessageLoop::current()); |
| 211 if (message == WM_NULL) { | 203 if (message == WM_NULL) { |
| 212 base::AutoLock auto_lock(ping_lock_); | 204 base::AutoLock auto_lock(ping_lock_); |
| 213 ++pings_; | 205 ++pings_; |
| 214 ping_event_.Signal(); | 206 ping_event_.Signal(); |
| 215 } | 207 } |
| 216 | 208 |
| 217 return false; // Pass through to DefWindowProc. | 209 return false; // Pass through to DefWindowProc. |
| 218 } | 210 } |
| 219 | 211 |
| 220 void CreateMessageWindowInWorkerThread(const base::string16& name, | 212 void CreateMessageWindowInWorkerThread(bool* success, |
| 221 bool* success, | |
| 222 base::WaitableEvent* created) { | 213 base::WaitableEvent* created) { |
| 223 message_window_.reset(new base::win::MessageWindow); | 214 message_window_.reset(new base::win::MessageWindow); |
| 224 *success = message_window_->CreateNamed( | 215 *success = message_window_->CreateNamed( |
| 225 base::Bind(&WindowHangMonitorTest::MessageCallback, | 216 base::Bind(&WindowHangMonitorTest::MessageCallback, |
| 226 base::Unretained(this)), | 217 base::Unretained(this)), |
| 227 name); | 218 L"MessageWindowName"); |
| 228 created->Signal(); | 219 created->Signal(); |
| 229 } | 220 } |
| 230 | 221 |
| 231 void DeleteMessageWindowInWorkerThread(base::WaitableEvent* deleted) { | 222 void DeleteMessageWindowInWorkerThread(base::WaitableEvent* deleted) { |
| 232 message_window_.reset(); | 223 message_window_.reset(); |
| 233 if (deleted) | 224 if (deleted) |
| 234 deleted->Signal(); | 225 deleted->Signal(); |
| 235 } | 226 } |
| 236 | 227 |
| 237 HangMonitorThread monitor_thread_; | 228 HangMonitorThread monitor_thread_; |
| 238 scoped_ptr<base::win::MessageWindow> message_window_; | 229 scoped_ptr<base::win::MessageWindow> message_window_; |
| 239 base::string16 window_name_; | |
| 240 base::Lock ping_lock_; | 230 base::Lock ping_lock_; |
| 241 base::WaitableEvent ping_event_; | 231 base::WaitableEvent ping_event_; |
| 242 size_t pings_; | 232 size_t pings_; |
| 243 base::Thread window_thread_; | 233 base::Thread window_thread_; |
| 244 | 234 |
| 245 DISALLOW_COPY_AND_ASSIGN(WindowHangMonitorTest); | 235 DISALLOW_COPY_AND_ASSIGN(WindowHangMonitorTest); |
| 246 }; | 236 }; |
| 247 | 237 |
| 248 } // namespace | 238 } // namespace |
| 249 | 239 |
| 250 TEST_F(WindowHangMonitorTest, NoWindow) { | 240 TEST_F(WindowHangMonitorTest, NoWindow) { |
| 251 base::CommandLine child_command_line = | 241 base::CommandLine child_command_line = |
| 252 base::GetMultiProcessTestChildBaseCommandLine(); | 242 base::GetMultiProcessTestChildBaseCommandLine(); |
| 253 child_command_line.AppendSwitchASCII(switches::kTestChildProcess, | 243 child_command_line.AppendSwitchASCII(switches::kTestChildProcess, |
| 254 "NoWindowChild"); | 244 "NoWindowChild"); |
| 255 base::Process process = | 245 base::Process process = |
| 256 base::LaunchProcess(child_command_line, base::LaunchOptions()); | 246 base::LaunchProcess(child_command_line, base::LaunchOptions()); |
| 257 ASSERT_TRUE(process.IsValid()); | 247 ASSERT_TRUE(process.IsValid()); |
| 258 | 248 |
| 259 base::ScopedClosureRunner terminate_process_runner( | 249 base::ScopedClosureRunner terminate_process_runner( |
| 260 base::Bind(base::IgnoreResult(&base::Process::Terminate), | 250 base::Bind(base::IgnoreResult(&base::Process::Terminate), |
| 261 base::Unretained(&process), 1, true)); | 251 base::Unretained(&process), 1, true)); |
| 262 | 252 |
| 263 monitor_thread().Start(process.Duplicate(), window_name()); | 253 monitor_thread().Start(process.Duplicate()); |
| 264 | 254 |
| 265 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | 255 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( |
| 266 base::TimeDelta::FromMilliseconds(150))); | 256 base::TimeDelta::FromMilliseconds(150))); |
| 267 | 257 |
| 268 terminate_process_runner.Reset(); | 258 terminate_process_runner.Reset(); |
| 269 | 259 |
| 270 ASSERT_EQ(WindowHangMonitor::WINDOW_NOT_FOUND, | 260 ASSERT_EQ(WindowHangMonitor::WINDOW_NOT_FOUND, |
| 271 monitor_thread().WaitForEvent()); | 261 monitor_thread().WaitForEvent()); |
| 272 | 262 |
| 273 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | 263 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( |
| 274 base::TimeDelta::FromMilliseconds(150))); | 264 base::TimeDelta::FromMilliseconds(150))); |
| 275 } | 265 } |
| 276 | 266 |
| 277 TEST_F(WindowHangMonitorTest, WindowBeforeWatcher) { | 267 TEST_F(WindowHangMonitorTest, WindowBeforeWatcher) { |
| 278 CreateMessageWindow(); | 268 CreateMessageWindow(); |
| 279 | 269 |
| 280 monitor_thread().Start(base::Process::Current(), window_name()); | 270 monitor_thread().Start(base::Process::Current()); |
| 281 | 271 |
| 282 WaitForPing(); | 272 WaitForPing(); |
| 283 | 273 |
| 284 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | 274 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( |
| 285 base::TimeDelta::FromMilliseconds(150))); | 275 base::TimeDelta::FromMilliseconds(150))); |
| 286 } | 276 } |
| 287 | 277 |
| 288 TEST_F(WindowHangMonitorTest, WindowBeforeDestroy) { | 278 TEST_F(WindowHangMonitorTest, WindowBeforeDestroy) { |
| 289 CreateMessageWindow(); | 279 CreateMessageWindow(); |
| 290 | 280 |
| 291 monitor_thread().Start(base::Process::Current(), window_name()); | 281 monitor_thread().Start(base::Process::Current()); |
| 292 | 282 |
| 293 WaitForPing(); | 283 WaitForPing(); |
| 294 | 284 |
| 295 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | 285 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( |
| 296 base::TimeDelta::FromMilliseconds(150))); | 286 base::TimeDelta::FromMilliseconds(150))); |
| 297 | 287 |
| 298 monitor_thread().DestroyWatcher(); | 288 monitor_thread().DestroyWatcher(); |
| 299 | 289 |
| 300 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(base::TimeDelta())); | 290 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(base::TimeDelta())); |
| 301 } | 291 } |
| 302 | 292 |
| 303 TEST_F(WindowHangMonitorTest, NoWindowBeforeDestroy) { | 293 TEST_F(WindowHangMonitorTest, NoWindowBeforeDestroy) { |
| 304 monitor_thread().Start(base::Process::Current(), window_name()); | 294 monitor_thread().Start(base::Process::Current()); |
| 305 | 295 |
| 306 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | 296 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( |
| 307 base::TimeDelta::FromMilliseconds(150))); | 297 base::TimeDelta::FromMilliseconds(150))); |
| 308 monitor_thread().DestroyWatcher(); | 298 monitor_thread().DestroyWatcher(); |
| 309 | 299 |
| 310 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(base::TimeDelta())); | 300 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(base::TimeDelta())); |
| 311 } | 301 } |
| 312 | 302 |
| 313 TEST_F(WindowHangMonitorTest, WatcherBeforeWindow) { | 303 TEST_F(WindowHangMonitorTest, WatcherBeforeWindow) { |
| 314 monitor_thread().Start(base::Process::Current(), window_name()); | 304 monitor_thread().Start(base::Process::Current()); |
| 315 | 305 |
| 316 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | 306 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( |
| 317 base::TimeDelta::FromMilliseconds(150))); | 307 base::TimeDelta::FromMilliseconds(150))); |
| 318 | 308 |
| 319 CreateMessageWindow(); | 309 CreateMessageWindow(); |
| 320 | 310 |
| 321 WaitForPing(); | 311 WaitForPing(); |
| 322 | 312 |
| 323 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | 313 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( |
| 324 base::TimeDelta::FromMilliseconds(150))); | 314 base::TimeDelta::FromMilliseconds(150))); |
| 325 } | 315 } |
| 326 | 316 |
| 327 TEST_F(WindowHangMonitorTest, DetectsWindowDisappearance) { | 317 TEST_F(WindowHangMonitorTest, DetectsWindowDisappearance) { |
| 328 CreateMessageWindow(); | 318 CreateMessageWindow(); |
| 329 | 319 |
| 330 monitor_thread().Start(base::Process::Current(), window_name()); | 320 monitor_thread().Start(base::Process::Current()); |
| 331 | 321 |
| 332 WaitForPing(); | 322 WaitForPing(); |
| 333 | 323 |
| 334 DeleteMessageWindow(); | 324 DeleteMessageWindow(); |
| 335 | 325 |
| 336 ASSERT_EQ(WindowHangMonitor::WINDOW_VANISHED, | 326 ASSERT_EQ(WindowHangMonitor::WINDOW_VANISHED, |
| 337 monitor_thread().WaitForEvent()); | 327 monitor_thread().WaitForEvent()); |
| 338 | 328 |
| 339 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | 329 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( |
| 340 base::TimeDelta::FromMilliseconds(150))); | 330 base::TimeDelta::FromMilliseconds(150))); |
| 341 } | 331 } |
| 342 | 332 |
| 343 TEST_F(WindowHangMonitorTest, DetectsWindowNameChange) { | |
| 344 // This test changes the title of the message window as a proxy for what | |
| 345 // happens if the window handle is reused for a different purpose. The latter | |
| 346 // is impossible to test in a deterministic fashion. | |
| 347 CreateMessageWindow(); | |
| 348 | |
| 349 monitor_thread().Start(base::Process::Current(), window_name()); | |
| 350 | |
| 351 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | |
| 352 base::TimeDelta::FromMilliseconds(150))); | |
| 353 | |
| 354 ASSERT_TRUE(::SetWindowText(message_window()->hwnd(), L"Gonsky")); | |
| 355 | |
| 356 ASSERT_EQ(WindowHangMonitor::WINDOW_VANISHED, | |
| 357 monitor_thread().WaitForEvent()); | |
| 358 } | |
| 359 | |
| 360 TEST_F(WindowHangMonitorTest, DetectsWindowHang) { | 333 TEST_F(WindowHangMonitorTest, DetectsWindowHang) { |
| 361 CreateMessageWindow(); | 334 CreateMessageWindow(); |
| 362 | 335 |
| 363 monitor_thread().Start(base::Process::Current(), window_name()); | 336 monitor_thread().Start(base::Process::Current()); |
| 364 | 337 |
| 365 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | 338 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( |
| 366 base::TimeDelta::FromMilliseconds(150))); | 339 base::TimeDelta::FromMilliseconds(150))); |
| 367 | 340 |
| 368 // Block the worker thread. | 341 // Block the worker thread. |
| 369 base::WaitableEvent hang(true, false); | 342 base::WaitableEvent hang(true, false); |
| 370 | 343 |
| 371 window_thread()->task_runner()->PostTask( | 344 window_thread()->task_runner()->PostTask( |
| 372 FROM_HERE, | 345 FROM_HERE, |
| 373 base::Bind(&base::WaitableEvent::Wait, base::Unretained(&hang))); | 346 base::Bind(&base::WaitableEvent::Wait, base::Unretained(&hang))); |
| 374 | 347 |
| 375 EXPECT_EQ(WindowHangMonitor::WINDOW_HUNG, | 348 EXPECT_EQ(WindowHangMonitor::WINDOW_HUNG, |
| 376 monitor_thread().WaitForEvent()); | 349 monitor_thread().WaitForEvent()); |
| 377 | 350 |
| 378 // Unblock the worker thread. | 351 // Unblock the worker thread. |
| 379 hang.Signal(); | 352 hang.Signal(); |
| 380 | 353 |
| 381 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | 354 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( |
| 382 base::TimeDelta::FromMilliseconds(150))); | 355 base::TimeDelta::FromMilliseconds(150))); |
| 383 } | 356 } |
| 384 | 357 |
| 385 } // namespace browser_watcher | 358 } // namespace browser_watcher |
| OLD | NEW |