Some folks have been reporting Chromium using 100% CPU on
relatively innocuous websites lately.  Once we had a reduction and a theory
about what was happening Mike Belshe figured out what the actual problem was
pretty quickly.  I wanted to record exactly what was happening for posterity
- feel free to skip this if you aren't interested.  Here's what the problem
ended up being:

When a timer from WebKit is scheduled, it is added to the thread's
ThreadTimer timer heap which then calls into the WebCore::SharedTimer
interface's setFireTime(double) function to schedule the timer to be fired.
 In Chromium, this is implemented in
WebKitClientImpl::setSharedTimerFireTime(double) in
webkit/glue/webkitclient_impl.cc.  This function looks at the specified fire
time and the current time as calculated by WTF::currentTime() and then calls
PostDelayedTask() on the thread's MessageLoop.  When the task fires, it
calls the WebKitClientImpl::DoTimeout callback which calls
into ThreadTimers::sharedTimerFiredInternal()
in WebCore/platform/ThreadTimers.cpp.  ThreadTimers calculates the current
time according to WTF::currentTime() and then compares that value to the
first timer in the timer heap and fires it if appropriate.  ThreadTimers
then calls WebCore::SharedTimer::setFireTime() with the next fire time.

So what's the problem?  WTF::currentTime() is implemented twice, once in
JavaScriptCore/wtf/CurrentTime.cpp and once in
webkit/api/src/ChromiumCurrentTime.cpp.  The latter simply defers to
base::Time::Now().ToDoubleT(), the former uses a somewhat different
algorithm to try to normalize what the system returns.  ThreadTimers.cpp was
linking against the JavaScriptCore/wtf/CurrentTime.cpp version and
webkitclient_impl.cc was linking against the
webkit/api/src/ChromiumCurrentTime.cpp version.  At times, the two can get
and stay fairly far out of sync (more than a few milliseconds).  In
particular, wtf/CurrentTime.cpp can return values that are a good bit lower
than ChromiumCurrentTime.cpp.  When this happens the next fire time
calculated in ThreadTimer can be lower (aka sooner) than the value that
ChromiumCurrentTime.cpp's WTF::currentTime() reports.  Then something like
this occurs:

- ThreadTimer::sharedTimerFiredInternal() looks at the head of the timer
heap and retrieves an interval of (for example) 1ms
- ThreadTimer::sharedTimerFiredInternal() calls
JavaScriptCore/wtf/CurrentTime.cpp's WTF::currentTime() and gets a 'current'
value X
- ThreadTimer::sharedTimerFiredInternal() calls
SharedTimer::setFireTime(X+1) which is implemented in WebKitClientImpl
- WebKitClientImpl::setSharedTimerFireTime() takes the input value (X+1) and
then calculates the current time. however, it uses
webkit/api/src/ChromiumCurrentTime.cpp's WTF::currentTime() which returns Y
- Since the clocks are skewed, Y happens to be greater than X+1.
 WebKitClientImpl checks for this and sets a timer with delay 0 instead of a
negative delay
- Control returns to the event loop which after a negligible delay invokes
the PostDelayedTask
- The task fires which calls...ThreadTimer::sharedTimerFiredInternal()
 Since the current time (according to JavaScriptCore/wtf/CurrentTime.cpp) is
not far enough along to actually fire the next timer in the heap, the entire
process repeats from the beginning.

The fix is to make sure that Chromium always links against the
webkit/api/src/ChromiumCurrentTime.cpp's WTF::currentTime() so that calls to
it are at least nondecreasing, which happened in WebKit r50173.

- James

--~--~---------~--~----~------------~-------~--~----~
Chromium Developers mailing list: chromium-dev@googlegroups.com 
View archives, change email options, or unsubscribe: 
    http://groups.google.com/group/chromium-dev
-~----------~----~----~----~------~----~------~--~---

Reply via email to