charmbracelet / bubbletea

A powerful little TUI framework 🏗

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Application deadlock when initial model's init panics

eolso opened this issue · comments

Describe the bug
With the current flow in *Program.Run(), the renderer isn't started until after the initial model has its .Init() function called. This currently causes an issue in the event that the initial model panics unexpectedly because the program is set to recover from panics, but can't finish shutting down because it's in a deadlock. I propose moving the p.renderer.start() to before the p.initialModel.Init() call in order to prevent this deadlock scenario. This will at least have the renderer start listening for a done signal on the context in the event of a panic.

I'm unsure if there are other unintended side effects from this change, but I don't see any from testing all of the examples.

To Reproduce
Running the code in the Source Code section will reproduce the issue. Instead of the expected panic being bubbled up, the app instead gets permanently stuck hanging waiting for

	// Stop the renderer before acquiring the mutex to avoid a deadlock.
	r.once.Do(func() {
		r.done <- struct{}{}
	})

to execute.

Source Code

package main

import tea "github.com/charmbracelet/bubbletea"

type model struct {
}

func (m model) Init() tea.Cmd {
	//TODO implement me
	panic("implement me")
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	//TODO implement me
	panic("implement me")
}

func (m model) View() string {
	//TODO implement me
	panic("implement me")
}

func main() {
	m := model{}
	if _, err := tea.NewProgram(m).Run(); err != nil {
		panic(err)
	}
}

Expected behavior

Caught panic:                                                                                                                                                                                                                                       
                                                                                                                                                                                                                                                    
implement me                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                    
Restoring terminal...   

Additional context
I have a fork with a branch created with the proposed change here.

Thanks for this; I can totally reproduce. Want to open a PR with your fork and then we can go from there?