Bug 940737 - Monitor Compositor thread hangs using BackgroundHangMonitor; r=bsmedberg r=BenWa

This commit is contained in:
Jim Chen 2013-12-04 21:24:28 -05:00
parent bbe7529af4
commit 9bac879d9e
8 changed files with 70 additions and 5 deletions

View File

@ -144,7 +144,17 @@ bool CompositorParent::CreateThread()
}
sCompositorThreadRefCount = 1;
sCompositorThread = new Thread("Compositor");
if (!sCompositorThread->Start()) {
Thread::Options options;
/* Timeout values are powers-of-two to enable us get better data.
128ms is chosen for transient hangs because 8Hz should be the minimally
acceptable goal for Compositor responsiveness (normal goal is 60Hz). */
options.transient_hang_timeout = 128; // milliseconds
/* 8192ms is chosen for permanent hangs because it's several seconds longer
than the default hang timeout on major platforms (about 5 seconds). */
options.permanent_hang_timeout = 8192; // milliseconds
if (!sCompositorThread->StartWithOptions(options)) {
delete sCompositorThread;
sCompositorThread = nullptr;
return false;

View File

@ -99,6 +99,8 @@ MessageLoop::MessageLoop(Type type)
#ifdef OS_WIN
os_modal_loop_(false),
#endif // OS_WIN
transient_hang_timeout_(0),
permanent_hang_timeout_(0),
next_sequence_num_(0) {
DCHECK(!current()) << "should only have one message loop per thread";
lazy_tls_ptr.Pointer()->Set(this);

View File

@ -269,6 +269,20 @@ public:
}
#endif // OS_WIN
// Set the timeouts for background hang monitoring.
// A value of 0 indicates there is no timeout.
void set_hang_timeouts(uint32_t transient_timeout_ms,
uint32_t permanent_timeout_ms) {
transient_hang_timeout_ = transient_timeout_ms;
permanent_hang_timeout_ = permanent_timeout_ms;
}
uint32_t transient_hang_timeout() const {
return transient_hang_timeout_;
}
uint32_t permanent_hang_timeout() const {
return permanent_hang_timeout_;
}
//----------------------------------------------------------------------------
protected:
struct RunState {
@ -420,6 +434,10 @@ public:
bool os_modal_loop_;
#endif
// Timeout values for hang monitoring
uint32_t transient_hang_timeout_;
uint32_t permanent_hang_timeout_;
// The next sequence number to use for delayed tasks.
int next_sequence_num_;

View File

@ -5,9 +5,12 @@
#include "base/message_pump_default.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/scoped_nsautorelease_pool.h"
#include "GeckoProfiler.h"
#include "mozilla/BackgroundHangMonitor.h"
namespace base {
MessagePumpDefault::MessagePumpDefault()
@ -18,13 +21,21 @@ MessagePumpDefault::MessagePumpDefault()
void MessagePumpDefault::Run(Delegate* delegate) {
DCHECK(keep_running_) << "Quit must have been called outside of Run!";
const MessageLoop* const loop = MessageLoop::current();
mozilla::BackgroundHangMonitor hangMonitor(
loop->thread_name().c_str(),
loop->transient_hang_timeout(),
loop->permanent_hang_timeout());
for (;;) {
ScopedNSAutoreleasePool autorelease_pool;
hangMonitor.NotifyActivity();
bool did_work = delegate->DoWork();
if (!keep_running_)
break;
hangMonitor.NotifyActivity();
did_work |= delegate->DoDelayedWork(&delayed_work_time_);
if (!keep_running_)
break;
@ -32,6 +43,7 @@ void MessagePumpDefault::Run(Delegate* delegate) {
if (did_work)
continue;
hangMonitor.NotifyActivity();
did_work = delegate->DoIdleWork();
if (!keep_running_)
break;
@ -40,11 +52,13 @@ void MessagePumpDefault::Run(Delegate* delegate) {
continue;
if (delayed_work_time_.is_null()) {
hangMonitor.NotifyWait();
PROFILER_LABEL("MessagePump", "Wait");
event_.Wait();
} else {
TimeDelta delay = delayed_work_time_ - TimeTicks::Now();
if (delay > TimeDelta()) {
hangMonitor.NotifyWait();
PROFILER_LABEL("MessagePump", "Wait");
event_.TimedWait(delay);
} else {

View File

@ -147,6 +147,8 @@ void Thread::ThreadMain() {
thread_id_ = PlatformThread::CurrentId();
PlatformThread::SetName(name_.c_str());
message_loop.set_thread_name(name_);
message_loop.set_hang_timeouts(startup_data_->options.transient_hang_timeout,
startup_data_->options.permanent_hang_timeout);
message_loop_ = &message_loop;
// Let the thread do extra initialization.

View File

@ -5,6 +5,7 @@
#ifndef BASE_THREAD_H_
#define BASE_THREAD_H_
#include <stdint.h>
#include <string>
#include "base/message_loop.h"
@ -28,9 +29,21 @@ class Thread : PlatformThread::Delegate {
// A value of 0 indicates that the default maximum should be used.
size_t stack_size;
Options() : message_loop_type(MessageLoop::TYPE_DEFAULT), stack_size(0) {}
// Specifies the transient and permanent hang timeouts for background hang
// monitoring. A value of 0 indicates there is no timeout.
uint32_t transient_hang_timeout;
uint32_t permanent_hang_timeout;
Options()
: message_loop_type(MessageLoop::TYPE_DEFAULT)
, stack_size(0)
, transient_hang_timeout(0)
, permanent_hang_timeout(0) {}
Options(MessageLoop::Type type, size_t size)
: message_loop_type(type), stack_size(size) {}
: message_loop_type(type)
, stack_size(size)
, transient_hang_timeout(0)
, permanent_hang_timeout(0) {}
};
// Constructor.

View File

@ -301,8 +301,12 @@ BackgroundHangThread::BackgroundHangThread(const char* aName,
uint32_t aMaxTimeoutMs)
: mManager(BackgroundHangManager::sInstance)
, mThreadID(PR_GetCurrentThread())
, mTimeout(PR_MillisecondsToInterval(aTimeoutMs))
, mMaxTimeout(PR_MillisecondsToInterval(aMaxTimeoutMs))
, mTimeout(aTimeoutMs == BackgroundHangMonitor::kNoTimeout
? PR_INTERVAL_NO_TIMEOUT
: PR_MillisecondsToInterval(aTimeoutMs))
, mMaxTimeout(aMaxTimeoutMs == BackgroundHangMonitor::kNoTimeout
? PR_INTERVAL_NO_TIMEOUT
: PR_MillisecondsToInterval(aMaxTimeoutMs))
, mInterval(mManager->mIntervalNow)
, mHangStart(mInterval)
, mHanging(false)

View File

@ -106,6 +106,8 @@ private:
RefPtr<BackgroundHangThread> mThread;
public:
static const uint32_t kNoTimeout = 0;
/**
* ThreadHangStatsIterator is used to iterate through the ThreadHangStats
* associated with each active monitored thread. Because of an internal