[GH-ISSUE #569] Deadlock when calling Application.Draw from within the event loop #416

Closed
opened 2026-03-04 01:04:47 +03:00 by kerem · 1 comment
Owner

Originally created by @forejtv on GitHub (Mar 3, 2021).
Original GitHub issue: https://github.com/rivo/tview/issues/569

The following code hangs after pressing Enter:

package main

import (
	"github.com/rivo/tview"
)

var app *tview.Application
var list *tview.List

func main() {
	app = tview.NewApplication()
	list = tview.NewList()

	list.AddItem("foo", "", 0, func() { app.Draw() })

	if err := app.SetRoot(list, true).SetFocus(list).Run(); err != nil {
		panic(err)
	}
}

The important part is the app.Draw() call within an event handler for list item. In my understanding of tview's internals, the issue is that the event handler runs from within the event loop and it waits for app.Draw() call to finish, but it will not be finished until the current event loop iteration (the even handler itself) finishes. Hence the execution hangs.

I am not sure if this is expected. If yes, then the the wiki statement at https://github.com/rivo/tview/wiki/concurrency which says

If they [event handlers] are invoked in response to a key event, you also don't need to call Application.Draw() as the application's main loop will do that for you."

could probably be strengthened to

If they are invoked in response to a key event, you must not call Application.Draw() as the application's main loop will do that for you, and calling Application.Draw() (or Application.QueueUpdate()) from within an event handler will lead to a deadlock."

Originally created by @forejtv on GitHub (Mar 3, 2021). Original GitHub issue: https://github.com/rivo/tview/issues/569 The following code hangs after pressing Enter: ``` package main import ( "github.com/rivo/tview" ) var app *tview.Application var list *tview.List func main() { app = tview.NewApplication() list = tview.NewList() list.AddItem("foo", "", 0, func() { app.Draw() }) if err := app.SetRoot(list, true).SetFocus(list).Run(); err != nil { panic(err) } } ``` The important part is the `app.Draw()` call within an event handler for list item. In my understanding of tview's internals, the issue is that the event handler runs from within the event loop and it waits for `app.Draw()` call to finish, but it will not be finished until the current event loop iteration (the even handler itself) finishes. Hence the execution hangs. I am not sure if this is expected. If yes, then the the wiki statement at https://github.com/rivo/tview/wiki/concurrency which says > If they [event handlers] are invoked in response to a key event, you also don't need to call Application.Draw() as the application's main loop will do that for you." could probably be strengthened to > If they are invoked in response to a key event, you must not call Application.Draw() as the application's main loop will do that for you, and calling Application.Draw() (or Application.QueueUpdate()) from within an event handler will lead to a deadlock."
kerem closed this issue 2026-03-04 01:04:48 +03:00
Author
Owner

@rivo commented on GitHub (Mar 11, 2021):

That's a good catch. I actually didn't realize calling Draw() from an event handler would lead to a deadlock. I was tempted to move Draw() into its own goroutine (which would resolve the deadlock, I believe) but at the moment, I don't know if that would result in any undesired side effects. For sure, it may lead to users calling the function in an event handler (as in your example), resulting in multiple (unnecessary) calls to Draw(). So it's probably better to just clarify it like you suggested.

I've modified the Wiki (and the function comments) accordingly. Thanks.

<!-- gh-comment-id:796901224 --> @rivo commented on GitHub (Mar 11, 2021): That's a good catch. I actually didn't realize calling `Draw()` from an event handler would lead to a deadlock. I was tempted to move `Draw()` into its own goroutine (which would resolve the deadlock, I believe) but at the moment, I don't know if that would result in any undesired side effects. For sure, it may lead to users calling the function in an event handler (as in your example), resulting in multiple (unnecessary) calls to `Draw()`. So it's probably better to just clarify it like you suggested. I've modified the Wiki (and the function comments) accordingly. Thanks.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/tview#416
No description provided.