QueueUpdateDraw can block forever when Run exits concurrently
Summary
Application.QueueUpdateDraw can block forever when Application.Run exits concurrently because the event loop stops without releasing callers waiting for their queued update to execute.
Expected behavior
A queued update should not wait forever once the application has stopped. It should either be released without running, or a cancellable update API should be provided.
Reproduction
Using tcell.NewSimulationScreen:
- Start app.Run in a goroutine.
- Concurrently call app.QueueUpdateDraw(func() {}) in another goroutine.
- Post tcell.NewEventError(errors.New("screen failed")) to end Run.
- Wait for the QueueUpdateDraw call to return.
I repeated this 1,000 times. On attempt 623, Run had returned but QueueUpdateDraw was still blocked after 200ms.
The essential pattern is: start Run in one goroutine; call QueueUpdateDraw in another; post EventError; wait for Run and then for the update call. The final wait can block forever.
Why this matters
The package documentation recommends QueueUpdate and QueueUpdateDraw for UI changes from background goroutines. A terminal application can encounter an input or screen error while background work is still producing updates. Without a shutdown-aware queue, that leaves a goroutine permanently blocked.
I can provide a standalone runnable reproducer or a regression test if useful.
Source: rivo/tview