Skip to content

Commit f5da8d0

Browse files
authored
fix: handle nested SequenceMsg in event loop and use sync.WaitGroup f… (#1463)
* fix: handle nested SequenceMsg in event loop and use sync.WaitGroup for BatchMsg This simplifies the handling of nested BatchMsg within sequenceMsg and ensures that all commands in a BatchMsg are executed before proceeding. It also replaces errgroup with sync.WaitGroup for better clarity and performance when waiting for multiple goroutines to finish. Fixes: #680 Supersedes: #843 * chore: examples: bump dependencies
1 parent c76509a commit f5da8d0

File tree

4 files changed

+51
-26
lines changed

4 files changed

+51
-26
lines changed

examples/go.mod

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ require (
2323
github.com/aymanbagabas/go-udiff v0.2.0 // indirect
2424
github.com/aymerick/douceur v0.2.0 // indirect
2525
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
26-
github.com/charmbracelet/x/ansi v0.9.2 // indirect
26+
github.com/charmbracelet/x/ansi v0.9.3 // indirect
2727
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
2828
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 // indirect
2929
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect
@@ -45,8 +45,7 @@ require (
4545
github.com/yuin/goldmark v1.7.8 // indirect
4646
github.com/yuin/goldmark-emoji v1.0.5 // indirect
4747
golang.org/x/net v0.38.0 // indirect
48-
golang.org/x/sync v0.14.0 // indirect
49-
golang.org/x/sys v0.33.0 // indirect
48+
golang.org/x/sys v0.34.0 // indirect
5049
golang.org/x/term v0.31.0 // indirect
5150
golang.org/x/text v0.24.0 // indirect
5251
)

examples/go.sum

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG
2424
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
2525
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE=
2626
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA=
27-
github.com/charmbracelet/x/ansi v0.9.2 h1:92AGsQmNTRMzuzHEYfCdjQeUzTrgE1vfO5/7fEVoXdY=
28-
github.com/charmbracelet/x/ansi v0.9.2/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
27+
github.com/charmbracelet/x/ansi v0.9.3 h1:BXt5DHS/MKF+LjuK4huWrC6NCvHtexww7dMayh6GXd0=
28+
github.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
2929
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
3030
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
3131
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
@@ -86,12 +86,10 @@ golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZ
8686
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
8787
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
8888
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
89-
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
90-
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
9189
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
9290
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
93-
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
94-
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
91+
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
92+
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
9593
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
9694
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
9795
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=

tea.go

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424

2525
"github.com/charmbracelet/x/term"
2626
"github.com/muesli/cancelreader"
27-
"golang.org/x/sync/errgroup"
2827
)
2928

3029
// ErrProgramPanic is returned by [Program.Run] when the program recovers from a panic.
@@ -473,11 +472,10 @@ func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) {
473472

474473
case BatchMsg:
475474
for _, cmd := range msg {
476-
select {
477-
case <-p.ctx.Done():
478-
return model, nil
479-
case cmds <- cmd:
475+
if cmd == nil {
476+
continue
480477
}
478+
go p.Send(cmd())
481479
}
482480
continue
483481

@@ -490,22 +488,31 @@ func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) {
490488
}
491489

492490
msg := cmd()
493-
if batchMsg, ok := msg.(BatchMsg); ok {
494-
g, _ := errgroup.WithContext(p.ctx)
495-
for _, cmd := range batchMsg {
491+
switch msg := msg.(type) {
492+
case BatchMsg:
493+
var wg sync.WaitGroup
494+
for _, cmd := range msg {
495+
if cmd == nil {
496+
continue
497+
}
498+
wg.Add(1)
496499
cmd := cmd
497-
g.Go(func() error {
500+
go func() {
501+
defer wg.Done()
498502
p.Send(cmd())
499-
return nil
500-
})
503+
}()
501504
}
502-
503-
//nolint:errcheck,gosec
504-
g.Wait() // wait for all commands from batch msg to finish
505-
continue
505+
wg.Wait()
506+
case sequenceMsg:
507+
for _, cmd := range msg {
508+
if cmd == nil {
509+
continue
510+
}
511+
p.Send(cmd())
512+
}
513+
default:
514+
p.Send(msg)
506515
}
507-
508-
p.Send(msg)
509516
}
510517
}()
511518

tea_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,27 @@ func TestTeaSequenceMsgWithBatchMsg(t *testing.T) {
424424
}
425425
}
426426

427+
func TestTeaNestedSequenceMsg(t *testing.T) {
428+
var buf bytes.Buffer
429+
var in bytes.Buffer
430+
431+
inc := func() Msg {
432+
return incrementMsg{}
433+
}
434+
435+
m := &testModel{}
436+
p := NewProgram(m, WithInput(&in), WithOutput(&buf))
437+
go p.Send(sequenceMsg{inc, Sequence(inc, inc), Quit})
438+
439+
if _, err := p.Run(); err != nil {
440+
t.Fatal(err)
441+
}
442+
443+
if m.counter.Load() != 3 {
444+
t.Fatalf("counter should be 3, got %d", m.counter.Load())
445+
}
446+
}
447+
427448
func TestTeaSend(t *testing.T) {
428449
var buf bytes.Buffer
429450
var in bytes.Buffer

0 commit comments

Comments
 (0)