use context instead of struct channel

pull/392/head
Jesse Duffield 2 years ago
parent 7f38cf2ab2
commit b0241c79db

@ -18,25 +18,23 @@ import (
func (gui *Gui) renderContainerLogsToMain(container *commands.Container) tasks.TaskFunc {
return gui.NewTickerTask(TickerTaskOpts{
Func: func(stop, notifyStopped chan struct{}) {
gui.renderContainerLogsToMainAux(container, stop, notifyStopped)
Func: func(ctx context.Context, notifyStopped chan struct{}) {
gui.renderContainerLogsToMainAux(container, ctx, notifyStopped)
},
Duration: time.Millisecond * 200,
// TODO: see why this isn't working (when switching from Top tab to Logs tab in the services panel, the tops tab's content isn't removed)
Before: func(stop chan struct{}) { gui.clearMainView() },
Before: func(ctx context.Context) { gui.clearMainView() },
Wrap: gui.Config.UserConfig.Gui.WrapMainPanel,
Autoscroll: true,
})
}
func (gui *Gui) renderContainerLogsToMainAux(container *commands.Container, stop, notifyStopped chan struct{}) {
func (gui *Gui) renderContainerLogsToMainAux(container *commands.Container, ctx context.Context, notifyStopped chan struct{}) {
gui.clearMainView()
defer func() {
notifyStopped <- struct{}{}
}()
ctx := stopIntoCtx(stop)
mainView := gui.Views.Main
if err := gui.writeContainerLogs(container, ctx, mainView); err != nil {
@ -49,7 +47,7 @@ func (gui *Gui) renderContainerLogsToMainAux(container *commands.Container, stop
defer ticker.Stop()
for {
select {
case <-stop:
case <-ctx.Done():
return
case <-ticker.C:
result, err := container.Inspect()

@ -212,7 +212,7 @@ func (gui *Gui) containerConfigStr(container *commands.Container) string {
func (gui *Gui) renderContainerStats(container *commands.Container) tasks.TaskFunc {
return gui.NewTickerTask(TickerTaskOpts{
Func: func(stop, notifyStopped chan struct{}) {
Func: func(ctx context.Context, notifyStopped chan struct{}) {
contents, err := presentation.RenderStats(gui.Config.UserConfig, container, gui.Views.Main.Width())
if err != nil {
_ = gui.createErrorPanel(err.Error())
@ -221,26 +221,15 @@ func (gui *Gui) renderContainerStats(container *commands.Container) tasks.TaskFu
gui.reRenderStringMain(contents)
},
Duration: time.Second,
Before: func(stop chan struct{}) { gui.clearMainView() },
Before: func(ctx context.Context) { gui.clearMainView() },
Wrap: false, // wrapping looks bad here so we're overriding the config value
Autoscroll: false,
})
}
// TODO: remove this and just use a context
func stopIntoCtx(stop chan struct{}) context.Context {
ctx, ctxCancel := context.WithCancel(context.Background())
go func() {
<-stop
ctxCancel()
}()
return ctx
}
func (gui *Gui) renderContainerTop(container *commands.Container) tasks.TaskFunc {
return gui.NewTickerTask(TickerTaskOpts{
Func: func(stop, notifyStopped chan struct{}) {
ctx := stopIntoCtx(stop)
Func: func(ctx context.Context, notifyStopped chan struct{}) {
contents, err := container.RenderTop(ctx)
if err != nil {
gui.RenderStringMain(err.Error())
@ -249,7 +238,7 @@ func (gui *Gui) renderContainerTop(container *commands.Container) tasks.TaskFunc
gui.reRenderStringMain(contents)
},
Duration: time.Second,
Before: func(stop chan struct{}) { gui.clearMainView() },
Before: func(ctx context.Context) { gui.clearMainView() },
Wrap: gui.Config.UserConfig.Gui.WrapMainPanel,
Autoscroll: false,
})

@ -76,13 +76,6 @@ func (gui *Gui) jumpToTopMain(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) onMainTabClick(tabIndex int) error {
gui.Log.Warn(tabIndex)
viewName := gui.currentViewName()
mainView := gui.Views.Main
if viewName == "main" && mainView.ParentView != nil {
viewName = mainView.ParentView.Name()
}
currentSidePanel, ok := gui.currentSidePanel()
if !ok {

@ -41,10 +41,6 @@ func (gui *Gui) getBindings(v *gocui.View) []*Binding {
}
}
// } else if v.ParentView != nil && binding.ViewName == v.ParentView.Name() {
// // only add this if we don't have our own matching binding
// bindingsPanel = append(bindingsPanel, binding)
// append dummy element to have a separator between
// panel and global keybindings
bindingsPanel = append(bindingsPanel, &Binding{})

@ -1,6 +1,7 @@
package panels
import (
"context"
"fmt"
"strings"
@ -74,7 +75,7 @@ type IGui interface {
IgnoreStrings() []string
Update(func() error)
QueueTask(f func(stop chan struct{})) error
QueueTask(f func(ctx context.Context)) error
}
func (self *SideListPanel[T]) HandleClick() error {

@ -2,6 +2,7 @@ package gui
import (
"bytes"
"context"
"path"
"strings"
@ -115,7 +116,7 @@ func (gui *Gui) renderAllLogs(_project *commands.Project) tasks.TaskFunc {
return gui.NewTask(TaskOpts{
Autoscroll: true,
Wrap: gui.Config.UserConfig.Gui.WrapMainPanel,
Func: func(stop chan struct{}) {
Func: func(ctx context.Context) {
gui.clearMainView()
cmd := gui.OSCommand.RunCustomCommand(
@ -132,7 +133,7 @@ func (gui *Gui) renderAllLogs(_project *commands.Project) tasks.TaskFunc {
_ = cmd.Start()
go func() {
<-stop
<-ctx.Done()
if err := gui.OSCommand.Kill(cmd); err != nil {
gui.Log.Error(err)
}

@ -1,6 +1,7 @@
package gui
import (
"context"
"fmt"
"time"
@ -106,8 +107,7 @@ func (gui *Gui) renderServiceStats(service *commands.Service) tasks.TaskFunc {
func (gui *Gui) renderServiceTop(service *commands.Service) tasks.TaskFunc {
return gui.NewTickerTask(TickerTaskOpts{
Func: func(stop, notifyStopped chan struct{}) {
ctx := stopIntoCtx(stop)
Func: func(ctx context.Context, notifyStopped chan struct{}) {
contents, err := service.RenderTop(ctx)
if err != nil {
gui.RenderStringMain(err.Error())
@ -116,7 +116,7 @@ func (gui *Gui) renderServiceTop(service *commands.Service) tasks.TaskFunc {
gui.reRenderStringMain(contents)
},
Duration: time.Second,
Before: func(stop chan struct{}) { gui.clearMainView() },
Before: func(ctx context.Context) { gui.clearMainView() },
Wrap: gui.Config.UserConfig.Gui.WrapMainPanel,
Autoscroll: false,
})

@ -1,12 +1,13 @@
package gui
import (
"context"
"time"
"github.com/jesseduffield/lazydocker/pkg/tasks"
)
func (gui *Gui) QueueTask(f func(stop chan struct{})) error {
func (gui *Gui) QueueTask(f func(ctx context.Context)) error {
return gui.taskManager.NewTask(f)
}
@ -19,13 +20,13 @@ type RenderStringTaskOpts struct {
type TaskOpts struct {
Autoscroll bool
Wrap bool
Func func(stop chan struct{})
Func func(ctx context.Context)
}
type TickerTaskOpts struct {
Duration time.Duration
Before func(stop chan struct{})
Func func(stop, notifyStopped chan struct{})
Before func(ctx context.Context)
Func func(ctx context.Context, notifyStopped chan struct{})
Autoscroll bool
Wrap bool
}
@ -34,10 +35,7 @@ func (gui *Gui) NewRenderStringTask(opts RenderStringTaskOpts) tasks.TaskFunc {
taskOpts := TaskOpts{
Autoscroll: opts.Autoscroll,
Wrap: opts.Wrap,
Func: func(stop chan struct{}) {
// GetStrContent may be a slow function, so we clear the main view first
// so that we're not seeing the previous tab's content appear.
gui.clearMainView()
Func: func(ctx context.Context) {
gui.RenderStringMain(opts.GetStrContent())
},
}
@ -55,12 +53,12 @@ func (gui *Gui) NewSimpleRenderStringTask(getContent func() string) tasks.TaskFu
}
func (gui *Gui) NewTask(opts TaskOpts) tasks.TaskFunc {
return func(stop chan struct{}) {
return func(ctx context.Context) {
mainView := gui.Views.Main
mainView.Autoscroll = opts.Autoscroll
mainView.Wrap = opts.Wrap
opts.Func(stop)
opts.Func(ctx)
}
}
@ -70,25 +68,25 @@ func (gui *Gui) NewTask(opts TaskOpts) tasks.TaskFunc {
func (gui *Gui) NewTickerTask(opts TickerTaskOpts) tasks.TaskFunc {
notifyStopped := make(chan struct{}, 10)
task := func(stop chan struct{}) {
task := func(ctx context.Context) {
if opts.Before != nil {
opts.Before(stop)
opts.Before(ctx)
}
tickChan := time.NewTicker(opts.Duration)
defer tickChan.Stop()
// calling f first so that we're not waiting for the first tick
opts.Func(stop, notifyStopped)
opts.Func(ctx, notifyStopped)
for {
select {
case <-notifyStopped:
gui.Log.Info("exiting ticker task due to notifyStopped channel")
return
case <-stop:
case <-ctx.Done():
gui.Log.Info("exiting ticker task due to stopped channel")
return
case <-tickChan.C:
gui.Log.Info("running ticker task again")
opts.Func(stop, notifyStopped)
opts.Func(ctx, notifyStopped)
}
}
}

@ -1,6 +1,7 @@
package tasks
import (
"context"
"fmt"
"time"
@ -19,15 +20,16 @@ type TaskManager struct {
}
type Task struct {
stop chan struct{}
ctx context.Context
cancel context.CancelFunc
stopped bool
stopMutex deadlock.Mutex
notifyStopped chan struct{}
Log *logrus.Entry
f func(chan struct{})
f func(ctx context.Context)
}
type TaskFunc func(stop chan struct{})
type TaskFunc func(ctx context.Context)
func NewTaskManager(log *logrus.Entry, translationSet *i18n.TranslationSet) *TaskManager {
return &TaskManager{Log: log, Tr: translationSet}
@ -54,7 +56,7 @@ func (t *TaskManager) Close() {
}
}
func (t *TaskManager) NewTask(f func(stop chan struct{})) error {
func (t *TaskManager) NewTask(f func(ctx context.Context)) error {
go func() {
t.taskIDMutex.Lock()
t.newTaskId++
@ -67,7 +69,7 @@ func (t *TaskManager) NewTask(f func(stop chan struct{})) error {
return
}
stop := make(chan struct{}, 1) // we don't want to block on this in case the task already returned
ctx, cancel := context.WithCancel(context.Background())
notifyStopped := make(chan struct{})
if t.currentTask != nil {
@ -77,14 +79,15 @@ func (t *TaskManager) NewTask(f func(stop chan struct{})) error {
}
t.currentTask = &Task{
stop: stop,
ctx: ctx,
cancel: cancel,
notifyStopped: notifyStopped,
Log: t.Log,
f: f,
}
go func() {
f(stop)
f(ctx)
t.Log.Info("returned from function, closing notifyStopped")
close(notifyStopped)
}()
@ -99,7 +102,8 @@ func (t *Task) Stop() {
if t.stopped {
return
}
close(t.stop)
t.cancel()
t.Log.Info("closed stop channel, waiting for notifyStopped message")
<-t.notifyStopped
t.Log.Info("received notifystopped message")
@ -109,28 +113,28 @@ func (t *Task) Stop() {
// NewTickerTask is a convenience function for making a new task that repeats some action once per e.g. second
// the before function gets called after the lock is obtained, but before the ticker starts.
// if you handle a message on the stop channel in f() you need to send a message on the notifyStopped channel because returning is not sufficient. Here, unlike in a regular task, simply returning means we're now going to wait till the next tick to run again.
func (t *TaskManager) NewTickerTask(duration time.Duration, before func(stop chan struct{}), f func(stop, notifyStopped chan struct{})) error {
func (t *TaskManager) NewTickerTask(duration time.Duration, before func(ctx context.Context), f func(ctx context.Context, notifyStopped chan struct{})) error {
notifyStopped := make(chan struct{}, 10)
return t.NewTask(func(stop chan struct{}) {
return t.NewTask(func(ctx context.Context) {
if before != nil {
before(stop)
before(ctx)
}
tickChan := time.NewTicker(duration)
defer tickChan.Stop()
// calling f first so that we're not waiting for the first tick
f(stop, notifyStopped)
f(ctx, notifyStopped)
for {
select {
case <-notifyStopped:
t.Log.Info("exiting ticker task due to notifyStopped channel")
return
case <-stop:
case <-ctx.Done():
t.Log.Info("exiting ticker task due to stopped cahnnel")
return
case <-tickChan.C:
t.Log.Info("running ticker task again")
f(stop, notifyStopped)
f(ctx, notifyStopped)
}
}
})

Loading…
Cancel
Save