fix data races (#230) (#235)

* fix data races (#230)

* Update CODEOWNERS
This commit is contained in:
rrrooommmaaa 2019-03-07 12:15:48 +03:00 committed by Max Risuhin
parent ae7c84b322
commit 7ac6c801e2
11 changed files with 124 additions and 22 deletions

2
.github/CODEOWNERS vendored
View File

@ -1 +1 @@
* @liamg * @liamg @MaxRis

View File

@ -173,15 +173,23 @@ func (gui *GUI) scale() float32 {
} }
// can only be called on OS thread // can only be called on OS thread
func (gui *GUI) resizeToTerminal(newCols uint, newRows uint) { func (gui *GUI) resizeToTerminal() {
if gui.window.GetAttrib(glfw.Iconified) != 0 { if gui.window.GetAttrib(glfw.Iconified) != 0 {
return return
} }
// Order of locking:
// 1. resizeLock
// 2. terminal's lock
gui.resizeLock.Lock() gui.resizeLock.Lock()
defer gui.resizeLock.Unlock() defer gui.resizeLock.Unlock()
gui.terminal.Lock()
defer gui.terminal.Unlock()
termCols, termRows := gui.terminal.GetSize()
newCols := uint(termCols)
newRows := uint(termRows)
cols, rows := gui.renderer.GetTermSize() cols, rows := gui.renderer.GetTermSize()
if cols == newCols && rows == newRows { if cols == newCols && rows == newRows {
return return
@ -244,9 +252,16 @@ func (gui *GUI) resize(w *glfw.Window, width int, height int) {
return return
} }
// Order of locking:
// 1. resizeLock
// 2. terminal's lock
terminalAlreadyLocked := false
if gui.internalResize == false { if gui.internalResize == false {
gui.resizeLock.Lock() gui.resizeLock.Lock()
defer gui.resizeLock.Unlock() defer gui.resizeLock.Unlock()
// No need to lock the terminal right away, we can lock it later
} else {
terminalAlreadyLocked = true
} }
gui.logger.Debugf("Initiating GUI resize to %dx%d", width, height) gui.logger.Debugf("Initiating GUI resize to %dx%d", width, height)
@ -268,6 +283,11 @@ func (gui *GUI) resize(w *glfw.Window, width int, height int) {
gui.logger.Debugf("Calculating size in cols/rows...") gui.logger.Debugf("Calculating size in cols/rows...")
cols, rows := gui.renderer.GetTermSize() cols, rows := gui.renderer.GetTermSize()
gui.logger.Debugf("Resizing internal terminal...") gui.logger.Debugf("Resizing internal terminal...")
if !terminalAlreadyLocked {
gui.terminal.Lock()
defer gui.terminal.Unlock()
terminalAlreadyLocked = true
}
if err := gui.terminal.SetSize(cols, rows); err != nil { if err := gui.terminal.SetSize(cols, rows); err != nil {
gui.logger.Errorf("Failed to resize terminal to %d cols, %d rows: %s", cols, rows, err) gui.logger.Errorf("Failed to resize terminal to %d cols, %d rows: %s", cols, rows, err)
} }
@ -278,11 +298,16 @@ func (gui *GUI) resize(w *glfw.Window, width int, height int) {
gui.logger.Debugf("Setting viewport size...") gui.logger.Debugf("Setting viewport size...")
gl.Viewport(0, 0, int32(gui.width), int32(gui.height)) gl.Viewport(0, 0, int32(gui.width), int32(gui.height))
if !terminalAlreadyLocked {
gui.terminal.Lock()
defer gui.terminal.Unlock()
terminalAlreadyLocked = true
}
gui.terminal.SetCharSize(gui.renderer.cellWidth, gui.renderer.cellHeight) gui.terminal.SetCharSize(gui.renderer.cellWidth, gui.renderer.cellHeight)
gui.logger.Debugf("Resize complete!") gui.logger.Debugf("Resize complete!")
gui.redraw() gui.redraw(!terminalAlreadyLocked)
gui.window.SwapBuffers() gui.window.SwapBuffers()
} }
@ -337,11 +362,11 @@ func (gui *GUI) Render() error {
gui.window.SetMouseButtonCallback(gui.mouseButtonCallback) gui.window.SetMouseButtonCallback(gui.mouseButtonCallback)
gui.window.SetCursorPosCallback(gui.mouseMoveCallback) gui.window.SetCursorPosCallback(gui.mouseMoveCallback)
gui.window.SetRefreshCallback(func(w *glfw.Window) { gui.window.SetRefreshCallback(func(w *glfw.Window) {
gui.terminal.SetDirty() gui.terminal.SetDirtyLocked()
}) })
gui.window.SetFocusCallback(func(w *glfw.Window, focused bool) { gui.window.SetFocusCallback(func(w *glfw.Window, focused bool) {
if focused { if focused {
gui.terminal.SetDirty() gui.terminal.SetDirtyLocked()
} }
}) })
gui.window.SetPosCallback(gui.windowPosChangeCallback) gui.window.SetPosCallback(gui.windowPosChangeCallback)
@ -354,6 +379,10 @@ func (gui *GUI) Render() error {
gui.resize(gui.window, w, h) gui.resize(gui.window, w, h)
} }
gui.terminal.AttachTitleChangeHandler(titleChan)
gui.terminal.AttachResizeHandler(resizeChan)
gui.terminal.AttachReverseHandler(reverseChan)
gui.logger.Debugf("Starting pty read handling...") gui.logger.Debugf("Starting pty read handling...")
go func() { go func() {
@ -372,10 +401,6 @@ func (gui *GUI) Render() error {
gl.Disable(gl.DEPTH_TEST) gl.Disable(gl.DEPTH_TEST)
gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gui.terminal.AttachTitleChangeHandler(titleChan)
gui.terminal.AttachResizeHandler(resizeChan)
gui.terminal.AttachReverseHandler(reverseChan)
ticker := time.NewTicker(time.Second) ticker := time.NewTicker(time.Second)
defer ticker.Stop() defer ticker.Stop()
@ -409,8 +434,7 @@ func (gui *GUI) Render() error {
case <-titleChan: case <-titleChan:
gui.window.SetTitle(gui.terminal.GetTitle()) gui.window.SetTitle(gui.terminal.GetTitle())
case <-resizeChan: case <-resizeChan:
cols, rows := gui.terminal.GetSize() gui.resizeToTerminal()
gui.resizeToTerminal(uint(cols), uint(rows))
case reverse := <-reverseChan: case reverse := <-reverseChan:
gui.generateDefaultCell(reverse) gui.generateDefaultCell(reverse)
forceRedraw = true forceRedraw = true
@ -421,7 +445,7 @@ func (gui *GUI) Render() error {
if gui.terminal.CheckDirty() || forceRedraw { if gui.terminal.CheckDirty() || forceRedraw {
gui.redraw() gui.redraw(true)
if gui.showDebugInfo { if gui.showDebugInfo {
gui.textbox(2, 2, fmt.Sprintf(`Cursor: %d,%d gui.textbox(2, 2, fmt.Sprintf(`Cursor: %d,%d
@ -441,7 +465,7 @@ Buffer Size: %d lines
if showMessage { if showMessage {
if latestVersion != "" && time.Since(startTime) < time.Second*10 && gui.terminal.ActiveBuffer().RawLine() == 0 { if latestVersion != "" && time.Since(startTime) < time.Second*10 && gui.terminal.ActiveBuffer().RawLine() == 0 {
time.AfterFunc(time.Second, gui.terminal.SetDirty) time.AfterFunc(time.Second, gui.terminal.SetDirtyLocked)
_, h := gui.terminal.GetSize() _, h := gui.terminal.GetSize()
var msg string var msg string
if version.Version == "" { if version.Version == "" {
@ -471,8 +495,11 @@ Buffer Size: %d lines
} }
func (gui *GUI) redraw() { func (gui *GUI) renderTerminalData(shouldLock bool) {
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT) if shouldLock {
gui.terminal.Lock()
defer gui.terminal.Unlock()
}
lines := gui.terminal.GetVisibleLines() lines := gui.terminal.GetVisibleLines()
lineCount := int(gui.terminal.ActiveBuffer().ViewHeight()) lineCount := int(gui.terminal.ActiveBuffer().ViewHeight())
colCount := int(gui.terminal.ActiveBuffer().ViewWidth()) colCount := int(gui.terminal.ActiveBuffer().ViewWidth())
@ -602,6 +629,11 @@ func (gui *GUI) redraw() {
} }
} }
}
func (gui *GUI) redraw(shouldLock bool) {
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)
gui.renderTerminalData(shouldLock)
gui.renderOverlay() gui.renderOverlay()
} }

View File

@ -5,7 +5,7 @@ type overlay interface {
} }
func (gui *GUI) setOverlay(m overlay) { func (gui *GUI) setOverlay(m overlay) {
defer gui.terminal.SetDirty() defer gui.terminal.SetDirtyLocked()
gui.overlay = m gui.overlay = m
} }

View File

@ -105,7 +105,11 @@ func TestCursorMovement(t *testing.T) {
send(term, "1\n") send(term, "1\n")
sleep() sleep()
if term.ActiveBuffer().CompareViewLines("vttest/test-cursor-movement-1") == false { term.Lock()
compareResult := term.ActiveBuffer().CompareViewLines("vttest/test-cursor-movement-1")
term.Unlock()
if compareResult == false {
os.Exit(terminate(fmt.Sprintf("ActiveBuffer doesn't match vttest template vttest/test-cursor-movement-1"))) os.Exit(terminate(fmt.Sprintf("ActiveBuffer doesn't match vttest template vttest/test-cursor-movement-1")))
} }

View File

@ -35,26 +35,42 @@ func swallowHandler(n int) func(pty chan rune, terminal *Terminal) error {
} }
func risHandler(pty chan rune, terminal *Terminal) error { func risHandler(pty chan rune, terminal *Terminal) error {
terminal.Lock()
defer terminal.Unlock()
terminal.ActiveBuffer().Clear() terminal.ActiveBuffer().Clear()
return nil return nil
} }
func indexHandler(pty chan rune, terminal *Terminal) error { func indexHandler(pty chan rune, terminal *Terminal) error {
terminal.Lock()
defer terminal.Unlock()
terminal.ActiveBuffer().Index() terminal.ActiveBuffer().Index()
return nil return nil
} }
func reverseIndexHandler(pty chan rune, terminal *Terminal) error { func reverseIndexHandler(pty chan rune, terminal *Terminal) error {
terminal.Lock()
defer terminal.Unlock()
terminal.ActiveBuffer().ReverseIndex() terminal.ActiveBuffer().ReverseIndex()
return nil return nil
} }
func saveCursorHandler(pty chan rune, terminal *Terminal) error { func saveCursorHandler(pty chan rune, terminal *Terminal) error {
// Handler should lock the terminal if there will be write operations to any data read by the renderer
// terminal.Lock()
// defer terminal.Unlock()
terminal.ActiveBuffer().SaveCursor() terminal.ActiveBuffer().SaveCursor()
return nil return nil
} }
func restoreCursorHandler(pty chan rune, terminal *Terminal) error { func restoreCursorHandler(pty chan rune, terminal *Terminal) error {
terminal.Lock()
defer terminal.Unlock()
terminal.ActiveBuffer().RestoreCursor() terminal.ActiveBuffer().RestoreCursor()
return nil return nil
} }
@ -73,11 +89,18 @@ func ansiHandler(pty chan rune, terminal *Terminal) error {
} }
func nextLineHandler(pty chan rune, terminal *Terminal) error { func nextLineHandler(pty chan rune, terminal *Terminal) error {
terminal.Lock()
defer terminal.Unlock()
terminal.ActiveBuffer().NewLineEx(true) terminal.ActiveBuffer().NewLineEx(true)
return nil return nil
} }
func tabSetHandler(pty chan rune, terminal *Terminal) error { func tabSetHandler(pty chan rune, terminal *Terminal) error {
// Handler should lock the terminal if there will be write operations to any data read by the renderer
// terminal.Lock()
// defer terminal.Unlock()
terminal.terminalState.TabSetAtCursor() terminal.terminalState.TabSetAtCursor()
return nil return nil
} }

View File

@ -54,6 +54,10 @@ func scs1Handler(pty chan rune, terminal *Terminal) error {
func scsHandler(pty chan rune, terminal *Terminal, which int) error { func scsHandler(pty chan rune, terminal *Terminal, which int) error {
b := <-pty b := <-pty
// Handler should lock the terminal if there will be write operations to any data read by the renderer
// terminal.Lock()
// defer terminal.Unlock()
cs, ok := charSets[b] cs, ok := charSets[b]
if ok { if ok {
terminal.logger.Debugf("Selected charset %v into G%v", string(b), which) terminal.logger.Debugf("Selected charset %v into G%v", string(b), which)

View File

@ -89,6 +89,9 @@ func splitParams(paramString string) []string {
func csiHandler(pty chan rune, terminal *Terminal) error { func csiHandler(pty chan rune, terminal *Terminal) error {
final, param, intermediate := loadCSI(pty) final, param, intermediate := loadCSI(pty)
terminal.Lock()
defer terminal.Unlock()
// process intermediate control codes before the CSI // process intermediate control codes before the CSI
for _, b := range intermediate { for _, b := range intermediate {
terminal.processRune(b) terminal.processRune(b)

View File

@ -72,6 +72,13 @@ func shiftInHandler(terminal *Terminal) error {
return nil return nil
} }
func (terminal *Terminal) processRuneLocked(b rune) {
terminal.Lock()
defer terminal.Unlock()
terminal.processRune(b)
}
func (terminal *Terminal) processRune(b rune) { func (terminal *Terminal) processRune(b rune) {
if handler, ok := runeMap[b]; ok { if handler, ok := runeMap[b]; ok {
if err := handler(terminal); err != nil { if err := handler(terminal); err != nil {
@ -103,6 +110,8 @@ func (terminal *Terminal) processInput(pty chan rune) {
var b rune var b rune
// debug := ""
for { for {
if terminal.config.Slomo { if terminal.config.Slomo {
@ -111,15 +120,19 @@ func (terminal *Terminal) processInput(pty chan rune) {
b = <-pty b = <-pty
// debug += fmt.Sprintf("0x%x ", b)
if b == 0x1b { if b == 0x1b {
// terminal.logger.Debug(debug)
// debug = ""
//terminal.logger.Debugf("Handling escape sequence: 0x%x", b) //terminal.logger.Debugf("Handling escape sequence: 0x%x", b)
if err := ansiHandler(pty, terminal); err != nil { if err := ansiHandler(pty, terminal); err != nil {
terminal.logger.Errorf("Error handling escape sequence: %s", err) terminal.logger.Errorf("Error handling escape sequence: %s", err)
} }
terminal.isDirty = true terminal.SetDirtyLocked()
continue continue
} }
terminal.processRune(b) terminal.processRuneLocked(b)
} }
} }

View File

@ -6,6 +6,9 @@ func screenStateHandler(pty chan rune, terminal *Terminal) error {
b := <-pty b := <-pty
switch b { switch b {
case '8': // DECALN -- Screen Alignment Pattern case '8': // DECALN -- Screen Alignment Pattern
terminal.Lock()
defer terminal.Unlock()
// hide cursor? // hide cursor?
buffer := terminal.ActiveBuffer() buffer := terminal.ActiveBuffer()
terminal.ResetVerticalMargins() terminal.ResetVerticalMargins()

View File

@ -131,6 +131,9 @@ func sixelHandler(pty chan rune, terminal *Terminal) error {
defer terminal.SetLineFeedMode() defer terminal.SetLineFeedMode()
} }
terminal.Lock()
defer terminal.Unlock()
drawSixel(six, terminal) drawSixel(six, terminal)
return nil return nil

View File

@ -105,6 +105,9 @@ func (terminal *Terminal) SetBracketedPasteMode(enabled bool) {
} }
func (terminal *Terminal) CheckDirty() bool { func (terminal *Terminal) CheckDirty() bool {
terminal.Lock()
defer terminal.Unlock()
d := terminal.isDirty d := terminal.isDirty
terminal.isDirty = false terminal.isDirty = false
return d || terminal.ActiveBuffer().IsDirty() return d || terminal.ActiveBuffer().IsDirty()
@ -355,9 +358,6 @@ func (terminal *Terminal) GetSize() (int, int) {
} }
func (terminal *Terminal) SetSize(newCols uint, newLines uint) error { func (terminal *Terminal) SetSize(newCols uint, newLines uint) error {
terminal.lock.Lock()
defer terminal.lock.Unlock()
if terminal.size.Width == uint16(newCols) && terminal.size.Height == uint16(newLines) { if terminal.size.Width == uint16(newCols) && terminal.size.Height == uint16(newLines) {
return nil return nil
} }
@ -420,3 +420,20 @@ func (terminal *Terminal) SetScreenMode(enabled bool) {
} }
terminal.emitReverse(enabled) terminal.emitReverse(enabled)
} }
func (terminal *Terminal) Lock() {
terminal.lock.Lock()
}
func (terminal *Terminal) Unlock() {
terminal.lock.Unlock()
}
// SetDirtyLocked sets dirty flag locking the terminal to prevent data race warnings
// @todo remove when switching to event-driven architecture
func (terminal *Terminal) SetDirtyLocked() {
terminal.Lock()
defer terminal.Unlock()
terminal.SetDirty()
}