Long loop in EventQueue::CoalesceEvents
🐞 Bug Report
Reproduction steps
Open up multiple tabs to pads on https://pad.riseup.net and let the browser idle for some days with the tabs in the background. After awhile, the browser will become unresponsive and the processes associated with these tabs will be spinning at 100% CPU utilisation and no new-tabs will do anything when a URL is typed in the url bar (i.e. the whole browser will seem semi soft-locked).
Expected behaviour
The browser should be responsive after some time away.
Actual behaviour
Browser becomes soft-locked until either restart or killing the busy processes in about:processes
Bookkeeping
- Browser version: 14.5a6
- Browser channel:
-
Release -
Alpha -
Nightly
-
- Distribution method:
-
Installer/archive from mullvad.net - .deb package
-
homebrew -
other (please specify):
-
- Operating System:
-
Windows -
macOS -
Linux -
Other (please specify):
-
- Operating System Version: Debian bookworm
Troubleshooting
I profile'd the offending 100% process and found that the process is stuck in EventQueue::CoalesceEvents()
in /accessible/base/EventQueue.cpp
. The relevant asm:
Basically, we seem to be stuck in a very long loop, probably handling the AccEvent::eCoalesceTextSelChange
case:
void EventQueue::CoalesceEvents() {
NS_ASSERTION(mEvents.Length(), "There should be at least one pending event!");
uint32_t tail = mEvents.Length() - 1;
AccEvent* tailEvent = mEvents[tail];
switch (tailEvent->mEventRule) {
...
case AccEvent::eCoalesceTextSelChange: {
// Coalesce older event by newer event for the same selection or target.
// Events for same selection may have different targets and vice versa one
// target may be pointed by different selections (for latter see
// bug 927159).
for (uint32_t index = tail - 1; index < tail; index--) {
AccEvent* thisEvent = mEvents[index];
if (thisEvent->mEventRule != AccEvent::eDoNotEmit &&
thisEvent->mEventType == tailEvent->mEventType) {
AccTextSelChangeEvent* thisTSCEvent = downcast_accEvent(thisEvent);
AccTextSelChangeEvent* tailTSCEvent = downcast_accEvent(tailEvent);
if (thisTSCEvent->mSel == tailTSCEvent->mSel ||
thisEvent->mAccessible == tailEvent->mAccessible) {
thisEvent->mEventRule = AccEvent::eDoNotEmit;
}
}
}
break; // eCoalesceTextSelChange
}
...
}
}
I initially thought this was due to the incorrect uint64_t vs uint32_t casting, but further review suggests this would result in incorrect behaviour when the number of pending events exceeds u32::MAX_VALUE, but not infinite loop behaviour. Regardless, I'll attach a debugger next time this happens and see what's actually going on.