Skip a flush that races between resize() and render() - #1785
Open
afonsojanu wants to merge 1 commit into
Open
Conversation
resize() updates the renderer's width/height and arms a redraw, but it never touches the stored view — that only happens afterward, when render() runs with a fresh View laid out for the new size. Both calls happen back to back in the same event loop iteration, but the ticker goroutine calls flush() independently at 60Hz, and if a tick lands in the gap between them, flush sees the new size paired with the previous size's view. It draws that stale content into a buffer already resized for the new dimensions, and the next tick then corrects it a frame later, which shows up as a brief ghost of the old layout at the new size. flush() now checks a flag that resize() sets and render() clears, and returns immediately without drawing while it's set. The pending erase and the width/height change stay in place, so once render() catches up (at most one tick later, since it always follows resize() in the same iteration), the very next flush draws the correct view at the correct size in one pass instead of two. Added a direct test on the renderer driving the exact sequence a race would produce: resize, then flush before render, then render, then flush again, checking that nothing reaches the output writer until render has actually caught up.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1780
resize()updates the renderer's width and height and arms a redraw, but it never touches the stored view — that only happens afterward, whenrender()runs with a freshViewlaid out for the new size. Both calls happen back to back in the same event loop iteration, but the ticker goroutine callsflush()independently at 60Hz, and if a tick lands in the gap between them,flush()sees the new size paired with the previous size's view. It draws that stale content into a buffer already resized for the new dimensions, and the next tick then corrects it a frame later — which is exactly the "ghost frame" the issue describes and traces in detail.flush()now checks aviewStaleflag thatresize()sets andrender()clears, and returns immediately without drawing while it's set. The pending erase and the width/height change stay in place, so oncerender()catches up (at most one tick later, since it always followsresize()in the same event loop iteration), the very nextflush()draws the correct view at the correct size in one pass instead of two.Added a direct test on the renderer driving the exact sequence the race produces: resize, flush before render, render, flush again — checking that nothing reaches the output writer during the gap and that the cell buffer only resizes once the correct view is available. Confirmed via
git stashthat this test fails (drawing the stale "old" view) without the fix and passes with it.go test ./...: all passing.go test -race ./...on the affected tests: clean.go vet ./...andgofmt -l: clean.golangci-lint run ./...: 0 issues.