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 -~----------~----~----~----~------~----~------~--~---