1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-04-21 12:16:54 +02:00

fix: fix goroutine leaks

This commit is contained in:
Ryooooooga 2023-01-03 23:07:16 +09:00
parent 1bb138c79c
commit 00b922604a
No known key found for this signature in database
GPG Key ID: 07CF200DFCC20C25
2 changed files with 37 additions and 17 deletions

View File

@ -1,6 +1,7 @@
package gui package gui
import ( import (
"context"
"fmt" "fmt"
"strings" "strings"
@ -16,8 +17,10 @@ import (
// This file is for the rendering of confirmation panels along with setting and handling associated // This file is for the rendering of confirmation panels along with setting and handling associated
// keybindings. // keybindings.
func (gui *Gui) wrappedConfirmationFunction(function func() error) func() error { func (gui *Gui) wrappedConfirmationFunction(cancel context.CancelFunc, function func() error) func() error {
return func() error { return func() error {
cancel()
if err := gui.c.PopContext(); err != nil { if err := gui.c.PopContext(); err != nil {
return err return err
} }
@ -32,8 +35,10 @@ func (gui *Gui) wrappedConfirmationFunction(function func() error) func() error
} }
} }
func (gui *Gui) wrappedPromptConfirmationFunction(function func(string) error, getResponse func() string) func() error { func (gui *Gui) wrappedPromptConfirmationFunction(cancel context.CancelFunc, function func(string) error, getResponse func() string) func() error {
return func() error { return func() error {
cancel()
if err := gui.c.PopContext(); err != nil { if err := gui.c.PopContext(); err != nil {
return err return err
} }
@ -111,11 +116,12 @@ func (gui *Gui) getConfirmationPanelWidth() int {
} }
func (gui *Gui) prepareConfirmationPanel( func (gui *Gui) prepareConfirmationPanel(
ctx context.Context,
opts types.ConfirmOpts, opts types.ConfirmOpts,
) error { ) error {
gui.Views.Confirmation.HasLoader = opts.HasLoader gui.Views.Confirmation.HasLoader = opts.HasLoader
if opts.HasLoader { if opts.HasLoader {
gui.g.StartTicking() gui.g.StartTicking(ctx)
} }
gui.Views.Confirmation.Title = opts.Title gui.Views.Confirmation.Title = opts.Title
// for now we do not support wrapping in our editor // for now we do not support wrapping in our editor
@ -145,16 +151,19 @@ func runeForMask(mask bool) rune {
return 0 return 0
} }
func (gui *Gui) createPopupPanel(opts types.CreatePopupPanelOpts) error { func (gui *Gui) createPopupPanel(ctx context.Context, opts types.CreatePopupPanelOpts) error {
gui.Mutexes.PopupMutex.Lock() gui.Mutexes.PopupMutex.Lock()
defer gui.Mutexes.PopupMutex.Unlock() defer gui.Mutexes.PopupMutex.Unlock()
ctx, cancel := context.WithCancel(ctx)
// we don't allow interruptions of non-loader popups in case we get stuck somehow // we don't allow interruptions of non-loader popups in case we get stuck somehow
// e.g. a credentials popup never gets its required user input so a process hangs // e.g. a credentials popup never gets its required user input so a process hangs
// forever. // forever.
// The proper solution is to have a queue of popup options // The proper solution is to have a queue of popup options
if gui.State.CurrentPopupOpts != nil && !gui.State.CurrentPopupOpts.HasLoader { if gui.State.CurrentPopupOpts != nil && !gui.State.CurrentPopupOpts.HasLoader {
gui.Log.Error("ignoring create popup panel because a popup panel is already open") gui.Log.Error("ignoring create popup panel because a popup panel is already open")
cancel()
return nil return nil
} }
@ -162,6 +171,7 @@ func (gui *Gui) createPopupPanel(opts types.CreatePopupPanelOpts) error {
gui.clearConfirmationViewKeyBindings() gui.clearConfirmationViewKeyBindings()
err := gui.prepareConfirmationPanel( err := gui.prepareConfirmationPanel(
ctx,
types.ConfirmOpts{ types.ConfirmOpts{
Title: opts.Title, Title: opts.Title,
Prompt: opts.Prompt, Prompt: opts.Prompt,
@ -171,6 +181,7 @@ func (gui *Gui) createPopupPanel(opts types.CreatePopupPanelOpts) error {
Mask: opts.Mask, Mask: opts.Mask,
}) })
if err != nil { if err != nil {
cancel()
return err return err
} }
confirmationView := gui.Views.Confirmation confirmationView := gui.Views.Confirmation
@ -185,11 +196,13 @@ func (gui *Gui) createPopupPanel(opts types.CreatePopupPanelOpts) error {
confirmationView.RenderTextArea() confirmationView.RenderTextArea()
} else { } else {
if err := gui.renderString(confirmationView, style.AttrBold.Sprint(opts.Prompt)); err != nil { if err := gui.renderString(confirmationView, style.AttrBold.Sprint(opts.Prompt)); err != nil {
cancel()
return err return err
} }
} }
if err := gui.setKeyBindings(opts); err != nil { if err := gui.setKeyBindings(cancel, opts); err != nil {
cancel()
return err return err
} }
@ -198,7 +211,7 @@ func (gui *Gui) createPopupPanel(opts types.CreatePopupPanelOpts) error {
return gui.c.PushContext(gui.State.Contexts.Confirmation) return gui.c.PushContext(gui.State.Contexts.Confirmation)
} }
func (gui *Gui) setKeyBindings(opts types.CreatePopupPanelOpts) error { func (gui *Gui) setKeyBindings(cancel context.CancelFunc, opts types.CreatePopupPanelOpts) error {
actions := utils.ResolvePlaceholderString( actions := utils.ResolvePlaceholderString(
gui.c.Tr.CloseConfirm, gui.c.Tr.CloseConfirm,
map[string]string{ map[string]string{
@ -210,13 +223,14 @@ func (gui *Gui) setKeyBindings(opts types.CreatePopupPanelOpts) error {
_ = gui.renderString(gui.Views.Options, actions) _ = gui.renderString(gui.Views.Options, actions)
var onConfirm func() error var onConfirm func() error
if opts.HandleConfirmPrompt != nil { if opts.HandleConfirmPrompt != nil {
onConfirm = gui.wrappedPromptConfirmationFunction(opts.HandleConfirmPrompt, func() string { return gui.Views.Confirmation.TextArea.GetContent() }) onConfirm = gui.wrappedPromptConfirmationFunction(cancel, opts.HandleConfirmPrompt, func() string { return gui.Views.Confirmation.TextArea.GetContent() })
} else { } else {
onConfirm = gui.wrappedConfirmationFunction(opts.HandleConfirm) onConfirm = gui.wrappedConfirmationFunction(cancel, opts.HandleConfirm)
} }
keybindingConfig := gui.c.UserConfig.Keybinding keybindingConfig := gui.c.UserConfig.Keybinding
onSuggestionConfirm := gui.wrappedPromptConfirmationFunction( onSuggestionConfirm := gui.wrappedPromptConfirmationFunction(
cancel,
opts.HandleConfirmPrompt, opts.HandleConfirmPrompt,
gui.getSelectedSuggestionValue, gui.getSelectedSuggestionValue,
) )
@ -235,7 +249,7 @@ func (gui *Gui) setKeyBindings(opts types.CreatePopupPanelOpts) error {
{ {
ViewName: "confirmation", ViewName: "confirmation",
Key: keybindings.GetKey(keybindingConfig.Universal.Return), Key: keybindings.GetKey(keybindingConfig.Universal.Return),
Handler: gui.wrappedConfirmationFunction(opts.HandleClose), Handler: gui.wrappedConfirmationFunction(cancel, opts.HandleClose),
}, },
{ {
ViewName: "confirmation", ViewName: "confirmation",
@ -260,7 +274,7 @@ func (gui *Gui) setKeyBindings(opts types.CreatePopupPanelOpts) error {
{ {
ViewName: "suggestions", ViewName: "suggestions",
Key: keybindings.GetKey(keybindingConfig.Universal.Return), Key: keybindings.GetKey(keybindingConfig.Universal.Return),
Handler: gui.wrappedConfirmationFunction(opts.HandleClose), Handler: gui.wrappedConfirmationFunction(cancel, opts.HandleClose),
}, },
{ {
ViewName: "suggestions", ViewName: "suggestions",

View File

@ -1,11 +1,12 @@
package popup package popup
import ( import (
"context"
"strings" "strings"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/common" "github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/gui/context" gctx "github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
@ -16,7 +17,7 @@ type PopupHandler struct {
*common.Common *common.Common
index int index int
deadlock.Mutex deadlock.Mutex
createPopupPanelFn func(types.CreatePopupPanelOpts) error createPopupPanelFn func(context.Context, types.CreatePopupPanelOpts) error
onErrorFn func() error onErrorFn func() error
popContextFn func() error popContextFn func() error
currentContextFn func() types.Context currentContextFn func() types.Context
@ -30,7 +31,7 @@ var _ types.IPopupHandler = &PopupHandler{}
func NewPopupHandler( func NewPopupHandler(
common *common.Common, common *common.Common,
createPopupPanelFn func(types.CreatePopupPanelOpts) error, createPopupPanelFn func(context.Context, types.CreatePopupPanelOpts) error,
onErrorFn func() error, onErrorFn func() error,
popContextFn func() error, popContextFn func() error,
currentContextFn func() types.Context, currentContextFn func() types.Context,
@ -96,7 +97,7 @@ func (self *PopupHandler) Confirm(opts types.ConfirmOpts) error {
self.index++ self.index++
self.Unlock() self.Unlock()
return self.createPopupPanelFn(types.CreatePopupPanelOpts{ return self.createPopupPanelFn(context.Background(), types.CreatePopupPanelOpts{
Title: opts.Title, Title: opts.Title,
Prompt: opts.Prompt, Prompt: opts.Prompt,
HandleConfirm: opts.HandleConfirm, HandleConfirm: opts.HandleConfirm,
@ -109,7 +110,7 @@ func (self *PopupHandler) Prompt(opts types.PromptOpts) error {
self.index++ self.index++
self.Unlock() self.Unlock()
return self.createPopupPanelFn(types.CreatePopupPanelOpts{ return self.createPopupPanelFn(context.Background(), types.CreatePopupPanelOpts{
Title: opts.Title, Title: opts.Title,
Prompt: opts.InitialContent, Prompt: opts.InitialContent,
Editable: true, Editable: true,
@ -127,12 +128,15 @@ func (self *PopupHandler) WithLoaderPanel(message string, f func() error) error
index = self.index index = self.index
self.Unlock() self.Unlock()
err := self.createPopupPanelFn(types.CreatePopupPanelOpts{ ctx, cancel := context.WithCancel(context.Background())
err := self.createPopupPanelFn(ctx, types.CreatePopupPanelOpts{
Prompt: message, Prompt: message,
HasLoader: true, HasLoader: true,
}) })
if err != nil { if err != nil {
self.Log.Error(err) self.Log.Error(err)
cancel()
return nil return nil
} }
@ -141,8 +145,10 @@ func (self *PopupHandler) WithLoaderPanel(message string, f func() error) error
self.Log.Error(err) self.Log.Error(err)
} }
cancel()
self.Lock() self.Lock()
if index == self.index && self.currentContextFn().GetKey() == context.CONFIRMATION_CONTEXT_KEY { if index == self.index && self.currentContextFn().GetKey() == gctx.CONFIRMATION_CONTEXT_KEY {
_ = self.popContextFn() _ = self.popContextFn()
} }
self.Unlock() self.Unlock()