Harden pad reclaim: trigger on dead space instead of absolute pad height #134
Reference in New Issue
Block a user
No description provided.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
fix/history-scroll-pad-redraw-stormfixes the history-scroll regression by raisingPAD_THRESHOLDfrom 3000 to 12000 — above the buffer line cap (PAD_SIZE - PAD_SIZE/10 = 9000) so the reclaimwin_redraw()never fires during normal scrolling. That is the correct quick fix and restores parity with the pre-merge behaviour, but the trigger is still an absolute pad-height threshold, which leaves one residual edge.Residual vulnerability (variant A, shipped)
_win_ensure_pad_capacity()re-enterswin_redraw()whenevercur_height >= PAD_THRESHOLD. After a reclaim redraw the cursor resets tobuffer->lines, so a per-message redraw storm reappears whenever the live buffer renders ≥PAD_THRESHOLDlines. That is reachable with a single message ≥ ~12000 rendered lines (a huge paste, a narrow terminal, or a crafted long incoming message — a mild DoS vector), because trimming cannot drop a single oversized entry below the cap. This edge already existed pre-merge at ~10000 via thebuffer.coverflow branch; raising the threshold mitigates but does not remove it. The constant is also a magic number decoupled fromPAD_SIZE/MESSAGES_TO_RETRIEVE.Proposed change (variant B, dead-space trigger)
Trigger the reclaim by how much dead space the pad holds (
getcury - buffer->lines), not by absolute height. Only the append path accumulates dead space (trimmed-oldest leaves a gap); the scroll/prepend path keepsgetcury == buffer->linesafter each redraw, so the trigger is structurally inert during scrolling regardless of buffer size — no per-message storm even on a giant single message (after a redrawdead == 0), and oversized messages can actually be displayed (the pad grows to fit) instead of being clipped at the cap.Sketch (same 4 edit sites as A, ~2 extra lines;
buffer->linesis already reachable inwindow.c):Why this is a separate, tested follow-up (not folded into the quick fix)
B introduces a reset trigger that did not exist before the merge. It is provably storm-free in analysis, but it has untested runtime dynamics that warrant real validation:
deadspikes by the trimmed amount when trimming fires mid-batch during scroll-up at the cap;DELTAmust exceed a typical batch so it does not cause a few extra redraws per page.getcuryadvances beforebuffer_prependupdatesbuffer->lines; the_in_redrawguard must still wrapwin_redraw().Acceptance criteria
*page_start adjusted to 0firing mid-history.buffer->lines + DELTA).Shared hardening (applies to both A and B)
wresize()return value; large pads can fail to allocate and silently drop content.win_redraw(wins_get_current())inbuffer.credraws the current window, not necessarily the one whose buffer overflowed (latent, pre-existing; mostly dead with the dynamic pad).