Skip to content

Fix PostEvent races, event buffer growth, and Text::Select cost - #1313

Merged
ArthurSonzogni merged 3 commits into
mainfrom
fix/event-loop-bugs
Jul 20, 2026
Merged

Fix PostEvent races, event buffer growth, and Text::Select cost#1313
ArthurSonzogni merged 3 commits into
mainfrom
fix/event-loop-bugs

Conversation

@ArthurSonzogni

Copy link
Copy Markdown
Owner

Three independent fixes, one commit each.

App::PostEvent thread safety. Documented as thread safe, but since the 7.0 event loop rework it pushed into the unsynchronized event buffer, racing with the main loop draining it. Now routed through the mutex-protected task queue; HandleTask already handles the Event variant identically. New test posts 10k events from another thread.

Unbounded event buffer growth. The setup receiver outlived setup with a frozen index. MultiReceiverBuffer::Prune() only frees below the minimum receiver index, so every subsequent event — including every mouse move — was retained for the App lifetime. The receiver is now scoped to InstallTerminalInfo.

Text::Select cost on large texts. It allocated one row entry per line of the whole text and scanned from line 0, every frame while a selection is active — defeating the #1309 optimization (~16MB allocated per frame on a million-line text). It now visits only the selection/box intersection. 100k lines with an active selection: 0.41ms → 0.011ms per frame. Also adds the first selection test for a multi-line text().

Note: trivial conflict with #1310 on PostEvent; the resolution is this branch's version (Post(Task(...))).

PostEvent pushed into the unsynchronized event buffer, racing with the
main loop when called from another thread. Route it through the
mutex-protected task queue instead.
The setup receiver outlived setup with a frozen index, pinning every
subsequent event in the buffer for the App lifetime. Scope it to
InstallTerminalInfo.
Select allocated one entry per line of the whole text and scanned from
line 0 on every frame while a selection is active. Store only the
selection/box intersection. 100k lines: 0.41ms -> 0.011ms per frame.
@github-actions

Copy link
Copy Markdown

⚠️ ABI Compatibility Report

Comparing main (base) with HEAD (PR)

ABI fingerprints changed from d0578d99d54909d813dd510a85d45a4a1c1f0d7e8f179b0eac88c644218de938 to c2329725cf72bb7ca3e8e32ec4c72973c06ebca688482499aef993725897181e.

Detailed ABI Analysis
Checking ABI changes between origin/main and HEAD...
Building origin/main...
Building HEAD...
--------------------------------------------------------------------------------
Comparing libftxui-screen.so...
No ABI changes detected for libftxui-screen.so.
--------------------------------------------------------------------------------
Comparing libftxui-dom.so...
No ABI changes detected for libftxui-dom.so.
--------------------------------------------------------------------------------
Comparing libftxui-component.so...
ABI changes detected for libftxui-component.so!

Summary of changes:
Functions changes summary: 0 Removed, 1 Changed (132 filtered out), 0 Added functions
Variables changes summary: 0 Removed, 0 Changed (162 filtered out), 0 Added variables

Detailed Report (filtered for ftxui::):
  [C] 'method void ftxui::App::Internal::Draw(ftxui::Component)' at app.cpp:264:1 has some indirect sub-type changes:
    implicit parameter 0 of type 'ftxui::App::Internal*' has sub-type changes:
      in pointed to type 'struct ftxui::App::Internal' at app.cpp:95:1:
          'std::unique_ptr<ftxui::MultiReceiverBuffer<ftxui::Event>::Receiver, std::default_delete<ftxui::MultiReceiverBuffer<ftxui::Event>::Receiver> > setup_receiver', at offset 6784 (in bits) at app.cpp:249:1
          'std::unique_ptr<ftxui::MultiReceiverBuffer<ftxui::Event>::Receiver, std::default_delete<ftxui::MultiReceiverBuffer<ftxui::Event>::Receiver> > main_loop_receiver' offset changed from 6848 to 6784 (in bits) (by -64 bits)
--------------------------------------------------------------------------------
No ABI changes detected.

@ArthurSonzogni
ArthurSonzogni merged commit 01bcb68 into main Jul 20, 2026
21 checks passed
@ArthurSonzogni
ArthurSonzogni deleted the fix/event-loop-bugs branch July 20, 2026 19:47
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.

1 participant