From 958dc20024479390c6375fc4ce514c55d3102371 Mon Sep 17 00:00:00 2001 From: Georgi Dimitrov Date: Sun, 5 Jun 2022 11:58:33 +0100 Subject: [PATCH] fix: race condition on repaint with alt screen I didn't realise at the time, but the tea.Program and the renderer share the mutex. This make the code difficult to reason about - it turns out the program sometimes acquires the lock and then calls the `setAltScreen` method of the renderer which in turns calls `repaint`. That causes a deadlock as `repaint` is trying to acquire the lock too. --- standard_renderer.go | 8 ++++++-- tea.go | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/standard_renderer.go b/standard_renderer.go index a929be9df0..4e754033d6 100644 --- a/standard_renderer.go +++ b/standard_renderer.go @@ -230,9 +230,7 @@ func (r *standardRenderer) write(s string) { } func (r *standardRenderer) repaint() { - r.mtx.Lock() r.lastRender = "" - r.mtx.Unlock() } func (r *standardRenderer) altScreen() bool { @@ -350,7 +348,9 @@ func (r *standardRenderer) handleMessages(msg Msg) { case repaintMsg: // Force a repaint by clearing the render cache as we slide into a // render. + r.mtx.Lock() r.repaint() + r.mtx.Unlock() case WindowSizeMsg: r.mtx.Lock() @@ -363,7 +363,9 @@ func (r *standardRenderer) handleMessages(msg Msg) { // Force a repaint on the area where the scrollable stuff was in this // update cycle + r.mtx.Lock() r.repaint() + r.mtx.Unlock() case syncScrollAreaMsg: // Re-render scrolling area @@ -372,7 +374,9 @@ func (r *standardRenderer) handleMessages(msg Msg) { r.insertTop(msg.lines, msg.topBoundary, msg.bottomBoundary) // Force non-scrolling stuff to repaint in this update cycle + r.mtx.Lock() r.repaint() + r.mtx.Unlock() case scrollUpMsg: r.insertTop(msg.lines, msg.topBoundary, msg.bottomBoundary) diff --git a/tea.go b/tea.go index 093e5f80ba..73be9bdc2b 100644 --- a/tea.go +++ b/tea.go @@ -503,7 +503,9 @@ func (p *Program) StartReturningModel() (Model, error) { continue case WindowSizeMsg: + p.mtx.Lock() p.renderer.repaint() + p.mtx.Unlock() case enterAltScreenMsg: p.EnterAltScreen()