Skip to content

LiveText: announce large bursts via a pumped generator#20177

Open
ethindp wants to merge 20 commits into
nvaccess:masterfrom
ethindp:live-text-queue-fixes
Open

LiveText: announce large bursts via a pumped generator#20177
ethindp wants to merge 20 commits into
nvaccess:masterfrom
ethindp:live-text-queue-fixes

Conversation

@ethindp
Copy link
Copy Markdown

@ethindp ethindp commented May 19, 2026

Link to issue number:

Fixes #6291. Partially addresses #15786, #15850, #11002, and #14189. All of these describe the same general class of "NVDA stops responding during console flood" problem but also include symptoms (UIA event flooding, NVDA crashes destroying the console window, etc.) that are out of scope for this PR and were partly addressed in #14888 and #14067 anyway. Related to #2977 and #875.

Most of these are already closed, but the underlying root cause (which is the main thread blocking during line announcement) was never actually addressed.

Summary of the issue:

When a live-text region (such as a terminal) produces a large burst of new text in a short window, NVDA's main thread blocks announcing every line sequentially, becoming unresponsive to keyboard input until the burst finishes. In some cases this lasts tens of seconds. In other (more drastic) cases, it can last so long that the user has to sign out, restart, or force-kill NVDA.

Description of user facing changes:

NVDA no longer freezes when large bursts of text are reported in a live region.

Description of developer facing changes:

LiveText._reportNewLines is no longer guaranteed to report lines synchronously. Internally, it now registers a generator with queueHandler which yields between batches of lines so the main thread can service input during long announcements.

A new class attribute LiveText.MAX_LINES caps the number of lines announced per burst. We drop everything before that on the floor.

When a new burst arrives while a previous one is still being announced, the previous generator is cancelled and the new burst supersedes it.

Description of development approach:

Live-text regions announce new text via LiveText._reportNewLines, which was invoked synchronously from the main thread's event queue. For workloads producing substantial amounts of new lines in a short window, every line traverses the full speech pipeline sequentially without yielding, which can leave the main thread unresponsive to keyboard input for tens of seconds or longer.

This PR addresses the responsiveness problem at the announcement layer rather than at the diff or event-source layer, which were previously addressed by #14888 and #14067. Specifically:

(1) Bursts larger than MAX_LINES are truncated to the most recent N lines. Skipping the oldest lines was chosen over skipping the newest because most flooding scenarios carry their actionable content at the end.

(2) Reporting now happens via a generator registered with queueHandler.registerGeneratorObject, yielding every 5 lines. This is the same primitive previously used by speech.speakSpelling and the original sayAll. Between yields, the main thread services the event queue, which keeps NVDA responsive.

(3) A handler is registered with pre_speechCanceled so that pressing control (or any other speech-cancel trigger) also cancels the in-flight generator. Perceptibly this may not cancel immediately and up to 5 lines may still be announced past the cancel, but internally the generator won't proceed past that iteration.

Testing strategy:

Reproducing this bug is rather tricky and I'm not sure what the most reliable reproducer is. However, here are some things that trigger it for me:

  1. In terminal, find a python project you haven't updated in a while and run uv sync --upgrade. Another alternative is to just uv add a bunch of packages. UV constantly sends ASCII control sequences and redraw commands to the terminal, and NVDA will freeze trying to process every single one.
  2. In power shell, dump the system event log with Get-EventLog -LogName System. Unless PowerShell has changed this command, this should freeze NVDA for at least a second.
  3. In an SSH session on a server or WSL2 instance, either:
    1. Dump the journal with journalctl -xfe, or
    2. On some systems like fedora, install a huge number of packages (> 5000) with a command like dnf install texlive-*.

I've no doubt there are many other ways this bug can be triggered. Essentially, any action that sends a huge number of speak requests to NVDA extremely quickly should do it.

Known issues with pull request:

This PR does not address the case where a single line in an editable text control is extremely long (e.g., the Notepad/Notepad++ scenario in #13432). The bottleneck there is getTextWithFields in NVDAObjects/window/edit.py, which does COM round-trips to extract the text and per-character formatting from the host process. In theory it could be addressed by chunking the speech and adding a fast path for uniform-format ranges, but that's a different PR with its own design questions.

The supersession behavior silently drops content from the previous burst when a new one arrives. In practice this is what users want for the common flooding scenarios (most recent state is most relevant), but I'm open to feedback if reviewers think a different policy is appropriate.

Code Review Checklist:

  • Documentation:
    • Change log entry
    • User Documentation
    • Developer / Technical Documentation
    • Context sensitive help for GUI changes
  • Testing:
    • Unit tests
    • System (end to end) tests
    • Manual testing
  • UX of all users considered:
    • Speech
    • Braille
    • Low Vision
    • Different web browsers
    • Localization in other languages / culture than English
  • API is compatible with existing add-ons.
  • Security precautions taken.

@cary-rowen
Copy link
Copy Markdown
Contributor

This looks like a super useful fix for anyone regularly running CLI-based AI agents. Might be worth taking this PR for a spin with a similar test case right away.

@ethindp
Copy link
Copy Markdown
Author

ethindp commented May 19, 2026

@cary-rowen I don't use agents myself, but I'd love more testing of it since I don't have a lot of cases to test this on my system atm. One candidate I'm 99 percent sure this solves is things like journalctl -xfe (or similar) where system logs get dumped to your terminal and you have to wade through the output.

@ethindp
Copy link
Copy Markdown
Author

ethindp commented May 19, 2026

@cary-rowen Just confirmed: this fixes the console flood problem with jorunalctl at least.

@ethindp ethindp marked this pull request as ready for review May 19, 2026 15:10
@ethindp ethindp requested a review from a team as a code owner May 19, 2026 15:10
@ethindp ethindp requested a review from SaschaCowley May 19, 2026 15:10
@seanbudd seanbudd requested review from michaelDCurran and seanbudd and removed request for SaschaCowley May 19, 2026 23:55
Comment thread source/NVDAObjects/behaviors.py
Comment thread source/NVDAObjects/behaviors.py Outdated
Comment thread source/NVDAObjects/behaviors.py Outdated
Comment thread source/NVDAObjects/behaviors.py Outdated
ethindp and others added 4 commits May 19, 2026 19:02
Comment thread source/NVDAObjects/behaviors.py Outdated
Comment thread source/NVDAObjects/behaviors.py Outdated
Comment thread source/NVDAObjects/behaviors.py Outdated
Comment thread source/NVDAObjects/behaviors.py Outdated
Comment thread source/NVDAObjects/behaviors.py Outdated
Comment thread source/NVDAObjects/behaviors.py Outdated
Co-authored-by: Sean Budd <seanbudd123@gmail.com>
@seanbudd
Copy link
Copy Markdown
Member

can this please get a change log entry and more clearer testing steps in the PR description?

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR targets a long-standing responsiveness issue where large bursts of live-region text (notably terminal output) can block NVDA’s main thread while announcing many lines. It changes LiveText line reporting to truncate very large bursts and to announce lines via a queued, yielding generator so the main loop can continue pumping input/events.

Changes:

  • Add a per-burst cap on announced live-text lines (default 100) and announce how many lines were skipped when truncation occurs.
  • Replace synchronous per-line reporting with a queueHandler-registered generator that yields every N lines to keep NVDA responsive.
  • Cancel any in-flight live-text reporting generator when speech is canceled or when a new burst supersedes it.

Comment thread source/NVDAObjects/behaviors.py
Comment thread source/NVDAObjects/behaviors.py Outdated
@ethindp
Copy link
Copy Markdown
Author

ethindp commented May 20, 2026

@seanbudd I can do the change log at least, but I'm not really sure what a "better testing strategy" entails. Like, you can validate it by SSH'ing into a server that's been up for a month or something and doing journalctl -xfe, but in general all the bugs I've looked at typically encounter this through a variety of circumstances.

@seanbudd
Copy link
Copy Markdown
Member

I was asking for a clearer testing strategy, not a better one.

The section currently states

Tested with specific ls commands on my computer in directories with very large numbers of files. In NVDA 2026.1, this lagged for a couple seconds before recovering. Admittedly I need to test this more thoroughly, hence the draft.

Can you provide brief step by step instructions on what you did to test?

@ethindp
Copy link
Copy Markdown
Author

ethindp commented May 21, 2026

All done, ready for merge I think

@seanbudd
Copy link
Copy Markdown
Member

doesn't look like any commits were pushed

@ethindp
Copy link
Copy Markdown
Author

ethindp commented May 21, 2026

Oh oops, I thought I'd pushed it but. Well. There we go.

@seanbudd
Copy link
Copy Markdown
Member

might have been a caching issue on my end

@ethindp
Copy link
Copy Markdown
Author

ethindp commented May 21, 2026

@seanbudd No, I hadn't pushed it xD

@codeofdusk
Copy link
Copy Markdown
Contributor

The one thing we might want to consider: the "xx lines skipped" is reported in the same stream as terminal output and can itself sound like a terminal line. Maybe adding a tone, pitch change, or other signal would more clearly flag this as an out-of-band notification?

@ethindp
Copy link
Copy Markdown
Author

ethindp commented May 21, 2026

@codeofdusk I honestly might remove the string on the basis that it just isn't audible to end-users when NVDA truncates the line list like that. And add-ons like speech history don't let you go back that far unless I'm misremembering, so the only way to "hear" it would be to log speech.

@ethindp
Copy link
Copy Markdown
Author

ethindp commented May 21, 2026

@codeofdusk Yeah, I just got rid of the announcement since as I noted in the commit it isn't audible when it happens. I'm not entirely certain why the system checks are failing though.

@codeofdusk
Copy link
Copy Markdown
Contributor

@ethindp I've actually heard the announcement in a few situations on my machine, particularly when reading long conversations in Gptcmd. I think a (perhaps configurable) BeepCommand is probably the most NVDA-idiomatic way to do this, but I'm happy to take that on in a follow-up.

The system test failures are probably transient, do git commit --allow-empty -m "bump CI" then git push.

@ethindp
Copy link
Copy Markdown
Author

ethindp commented May 21, 2026

@codeofdusk Yeah I just pushed the change without the announcement for now, unless you want me to revert that? I'm a bit hesitant to add a fully configurable beep and all that given that I don't know the NVDA codebase yet as much as I would like. But I'd be happy to see about working on it in another PR.

@codeofdusk
Copy link
Copy Markdown
Contributor

I'm happy to do that in a follow-up PR.

@ethindp
Copy link
Copy Markdown
Author

ethindp commented May 21, 2026

Ah alright, sounds good. I think this one might be finished then, I'm... Not really sure what else that needs be done.

@wmhn1872265132
Copy link
Copy Markdown
Contributor

You also need to resolve merge conflicts with the master branch, merge the upstream master branch into your branch, and resolve the merge conflicts.

@ethindp ethindp requested a review from a team as a code owner May 22, 2026 01:49
@ethindp ethindp requested a review from Qchristensen May 22, 2026 01:49
@wmhn1872265132
Copy link
Copy Markdown
Contributor

Your merging method seems to be problematic. The differences in this PR are very large; please do not use the squash method to merge.

@ethindp
Copy link
Copy Markdown
Author

ethindp commented May 22, 2026

@wmhn1872265132 I mean, okay, if that's what you want...

@ethindp
Copy link
Copy Markdown
Author

ethindp commented May 23, 2026

Is there anything else that is required for this PR? Just checking since I'm pretty sure people are clamoring for this to get merged. :D I hope all the testing has worked out well (it seems to work fantastically for me).

Comment thread source/NVDAObjects/behaviors.py Outdated
Comment thread source/NVDAObjects/behaviors.py Outdated
@cary-rowen
Copy link
Copy Markdown
Contributor

Thanks @ethindp
Could you update the PR description to match the latest changes?

ethindp and others added 2 commits May 23, 2026 09:15
Co-authored-by: wencong <manchen_0528@outlook.com>
Co-authored-by: wencong <manchen_0528@outlook.com>
@ethindp
Copy link
Copy Markdown
Author

ethindp commented May 23, 2026

Will do

@ethindp
Copy link
Copy Markdown
Author

ethindp commented May 23, 2026

@cary-rowen All done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NVDA hangs up in terminal, when a large piece of text is loaded

6 participants