Index: chrome/gpu/gpu_watchdog_thread.cc |
=================================================================== |
--- chrome/gpu/gpu_watchdog_thread.cc (revision 67749) |
+++ chrome/gpu/gpu_watchdog_thread.cc (working copy) |
@@ -18,16 +18,32 @@ |
} |
} |
-GpuWatchdogThread::GpuWatchdogThread(MessageLoop* watched_message_loop, |
- int timeout) |
+GpuWatchdogThread::GpuWatchdogThread(int timeout) |
: base::Thread("Watchdog"), |
- watched_message_loop_(watched_message_loop), |
+ watched_message_loop_(MessageLoop::current()), |
timeout_(timeout), |
armed_(false), |
+#if defined(OS_WIN) |
+ watched_thread_handle_(0), |
+ arm_time_(0), |
+#endif |
ALLOW_THIS_IN_INITIALIZER_LIST(task_observer_(this)) { |
- DCHECK(watched_message_loop); |
DCHECK(timeout >= 0); |
+#if defined(OS_WIN) |
+ // GetCurrentThread returns a pseudo-handle that cannot be used by one thread |
+ // to identify another. DuplicateHandle creates a "real" handle that can be |
+ // used for this purpose. |
+ BOOL result = DuplicateHandle(GetCurrentProcess(), |
+ GetCurrentThread(), |
+ GetCurrentProcess(), |
+ &watched_thread_handle_, |
+ THREAD_QUERY_INFORMATION, |
+ FALSE, |
+ 0); |
+ DCHECK(result); |
+#endif |
+ |
watched_message_loop_->AddTaskObserver(&task_observer_); |
} |
@@ -36,6 +52,10 @@ |
// implicitly by the destructor, CleanUp() will not be called. |
DCHECK(!method_factory_.get()); |
+#if defined(OS_WIN) |
+ CloseHandle(watched_thread_handle_); |
+#endif |
+ |
watched_message_loop_->RemoveTaskObserver(&task_observer_); |
} |
@@ -59,9 +79,6 @@ |
// The method factory must be destroyed on the watchdog thread. |
method_factory_->RevokeAll(); |
method_factory_.reset(); |
- |
- // Prevent any more delayed tasks from being posted. |
- watched_message_loop_ = NULL; |
} |
GpuWatchdogThread::GpuWatchdogTaskObserver::GpuWatchdogTaskObserver( |
@@ -106,39 +123,87 @@ |
armed_ = false; |
// The monitored thread has responded. Post a task to check it again. |
- if (watched_message_loop_) { |
- message_loop()->PostDelayedTask( |
- FROM_HERE, |
- method_factory_->NewRunnableMethod(&GpuWatchdogThread::OnCheck), |
- kCheckPeriod); |
- } |
+ message_loop()->PostDelayedTask( |
+ FROM_HERE, |
+ method_factory_->NewRunnableMethod(&GpuWatchdogThread::OnCheck), |
+ kCheckPeriod); |
} |
+#if defined(OS_WIN) |
+int64 GpuWatchdogThread::GetWatchedThreadTime() { |
+ FILETIME creation_time; |
+ FILETIME exit_time; |
+ FILETIME user_time; |
+ FILETIME kernel_time; |
+ BOOL result = GetThreadTimes(watched_thread_handle_, |
+ &creation_time, |
+ &exit_time, |
+ &kernel_time, |
+ &user_time); |
+ DCHECK(result); |
+ |
+ ULARGE_INTEGER user_time64; |
+ user_time64.HighPart = user_time.dwHighDateTime; |
+ user_time64.LowPart = user_time.dwLowDateTime; |
+ |
+ ULARGE_INTEGER kernel_time64; |
+ kernel_time64.HighPart = kernel_time.dwHighDateTime; |
+ kernel_time64.LowPart = kernel_time.dwLowDateTime; |
+ |
+ // Time is reported in units of 100 nanoseconds. Kernel and user time are |
+ // summed to deal with to kinds of hangs. One is where the GPU process is |
+ // stuck in user level, never calling into the kernel and kernel time is |
+ // not increasing. The other is where either the kernel hangs and never |
+ // returns to user level or where user level code |
+ // calls into kernel level repeatedly, giving up its quanta before it is |
+ // tracked, for example a loop that repeatedly Sleeps. |
+ return static_cast<int64>( |
+ (user_time64.QuadPart + kernel_time64.QuadPart) / 10000); |
+} |
+#endif |
+ |
void GpuWatchdogThread::OnCheck() { |
- if (watched_message_loop_) { |
- // Must set armed before posting the task. This task might be the only task |
- // that will activate the TaskObserver on the watched thread and it must not |
- // miss the false -> true transition. |
- armed_ = true; |
+ if (armed_) |
+ return; |
- // Post a task to the monitored thread that does nothing but wake up the |
- // TaskObserver. Any other tasks that are pending on the watched thread will |
- // also wake up the observer. This simply ensures there is at least one. |
- watched_message_loop_->PostTask( |
- FROM_HERE, |
- NewRunnableFunction(DoNothing)); |
+ // Must set armed before posting the task. This task might be the only task |
+ // that will activate the TaskObserver on the watched thread and it must not |
+ // miss the false -> true transition. |
+ armed_ = true; |
- // Post a task to the watchdog thread to exit if the monitored thread does |
- // not respond in time. |
+#if defined(OS_WIN) |
+ arm_time_ = GetWatchedThreadTime(); |
+#endif |
+ |
+ // Post a task to the monitored thread that does nothing but wake up the |
+ // TaskObserver. Any other tasks that are pending on the watched thread will |
+ // also wake up the observer. This simply ensures there is at least one. |
+ watched_message_loop_->PostTask( |
+ FROM_HERE, |
+ NewRunnableFunction(DoNothing)); |
+ |
+ // Post a task to the watchdog thread to exit if the monitored thread does |
+ // not respond in time. |
+ message_loop()->PostDelayedTask( |
+ FROM_HERE, |
+ method_factory_->NewRunnableMethod(&GpuWatchdogThread::OnExit), |
+ timeout_); |
+} |
+ |
+// Use the --disable-gpu-watchdog command line switch to disable this. |
+void GpuWatchdogThread::OnExit() { |
+#if defined(OS_WIN) |
+ // Defer termination until a certain amount of user time has elapsed. |
+ int64 time_since_arm = GetWatchedThreadTime() - arm_time_; |
+ if (time_since_arm < timeout_) { |
message_loop()->PostDelayedTask( |
FROM_HERE, |
method_factory_->NewRunnableMethod(&GpuWatchdogThread::OnExit), |
- timeout_); |
+ timeout_ - time_since_arm); |
+ return; |
} |
-} |
+#endif |
-// Use the --disable-gpu-watchdog command line switch to disable this. |
-void GpuWatchdogThread::OnExit() { |
// Make sure the timeout period is on the stack before crashing. |
volatile int timeout = timeout_; |