Index: content/gpu/gpu_watchdog_thread.cc |
diff --git a/content/gpu/gpu_watchdog_thread.cc b/content/gpu/gpu_watchdog_thread.cc |
index 22982db10489fcd6e00d6a9eb8a5f45c95a7f7dc..4cfde4f94b51803d2866226284431054230bd223 100644 |
--- a/content/gpu/gpu_watchdog_thread.cc |
+++ b/content/gpu/gpu_watchdog_thread.cc |
@@ -42,6 +42,10 @@ |
timeout_(base::TimeDelta::FromMilliseconds(timeout)), |
armed_(false), |
task_observer_(this), |
+#if defined(OS_WIN) |
+ watched_thread_handle_(0), |
+ arm_cpu_time_(), |
+#endif |
suspended_(false), |
#if defined(USE_X11) |
display_(NULL), |
@@ -50,6 +54,20 @@ |
#endif |
weak_factory_(this) { |
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 |
#if defined(OS_CHROMEOS) |
tty_file_ = base::OpenFile(base::FilePath(kTtyFilePath), "r"); |
@@ -106,6 +124,10 @@ |
// implicitly by the destructor, CleanUp() will not be called. |
DCHECK(!weak_factory_.HasWeakPtrs()); |
+#if defined(OS_WIN) |
+ CloseHandle(watched_thread_handle_); |
+#endif |
+ |
base::PowerMonitor* power_monitor = base::PowerMonitor::Get(); |
if (power_monitor) |
power_monitor->RemoveObserver(this); |
@@ -163,6 +185,10 @@ |
// that will activate the TaskObserver on the watched thread and it must not |
// miss the false -> true transition. |
armed_ = true; |
+ |
+#if defined(OS_WIN) |
+ arm_cpu_time_ = GetWatchedThreadTime(); |
+#endif |
// Immediately after the computer is woken up from being suspended it might |
// be pretty sluggish, so allow some extra time before the next timeout. |
@@ -188,6 +214,21 @@ |
void GpuWatchdogThread::DeliberatelyTerminateToRecoverFromHang() { |
// Should not get here while the system is suspended. |
DCHECK(!suspended_); |
+ |
+#if defined(OS_WIN) |
+ // Defer termination until a certain amount of CPU time has elapsed on the |
+ // watched thread. |
+ base::TimeDelta time_since_arm = GetWatchedThreadTime() - arm_cpu_time_; |
+ if (time_since_arm < timeout_) { |
+ message_loop()->PostDelayedTask( |
+ FROM_HERE, |
+ base::Bind( |
+ &GpuWatchdogThread::DeliberatelyTerminateToRecoverFromHang, |
+ weak_factory_.GetWeakPtr()), |
+ timeout_ - time_since_arm); |
+ return; |
+ } |
+#endif |
// If the watchdog woke up significantly behind schedule, disarm and reset |
// the watchdog check. This is to prevent the watchdog thread from terminating |
@@ -329,4 +370,37 @@ |
OnCheck(true); |
} |
+#if defined(OS_WIN) |
+base::TimeDelta 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 base::TimeDelta::FromMilliseconds(static_cast<int64_t>( |
+ (user_time64.QuadPart + kernel_time64.QuadPart) / 10000)); |
+} |
+#endif |
+ |
} // namespace content |