Skip to content

Commit 6a01212

Browse files
committed
fix race condition
1 parent ca671a1 commit 6a01212

File tree

2 files changed

+71
-35
lines changed

2 files changed

+71
-35
lines changed

main.go

Lines changed: 64 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -82,21 +82,29 @@ func newTui() *tui {
8282
func (t *tui) start() int {
8383
t.setAction()
8484

85-
t.stdinPane.reset()
86-
t.stdoutPane.reset()
85+
stdinCtx, stdinCancel := context.WithCancel(t.stdinPane.ctx)
86+
stdoutCtx, stdoutCancel := context.WithCancel(t.stdoutPane.ctx)
8787

8888
p := t.cliPane.prompt
89+
t.cliPane.syncUpdate(func() {
90+
t.cliPane.wg.Add(1)
91+
})
8992
go func() {
93+
defer t.cliPane.wg.Done()
94+
defer stdinCancel()
95+
defer stdoutCancel()
9096
if p == "" {
9197
t.stdinPane.setData(stdinBytes)
9298
} else {
93-
t.stdinPane.execCommand(p, stdinBytes)
99+
t.stdinPane.execCommand(stdinCtx, p, stdinBytes)
94100
}
95101
var text string
96102
t.QueueUpdate(func() {
97103
text = t.cliPane.GetText()
98104
})
99-
t.stdoutPane.execCommand(text, t.stdinPane.data)
105+
t.stdinPane.syncUpdate(func() {
106+
t.stdoutPane.execCommand(stdoutCtx, text, t.stdinPane.data)
107+
})
100108
}()
101109

102110
if err := t.Run(); err != nil {
@@ -113,9 +121,16 @@ func (t *tui) setAction() {
113121
return
114122
}
115123
t.stdoutPane.reset()
124+
stdoutCtx, stdoutCancel := context.WithCancel(t.stdoutPane.ctx)
116125

117126
go func() {
118-
t.stdoutPane.execCommand(text, t.stdinPane.data)
127+
defer stdoutCancel()
128+
t.cliPane.syncUpdate(func() {
129+
t.cliPane.wg.Wait()
130+
})
131+
t.stdinPane.syncUpdate(func() {
132+
t.stdoutPane.execCommand(stdoutCtx, text, t.stdinPane.data)
133+
})
119134
}()
120135
})
121136

@@ -140,6 +155,7 @@ func (t *tui) setAction() {
140155
t.stdinPane.cancel()
141156
t.stdoutPane.cancel()
142157
t.Stop()
158+
143159
if commandFlag {
144160
fmt.Println(adjustPipe(t.cliPane.prompt) + t.cliPane.GetText())
145161
return nil
@@ -150,7 +166,6 @@ func (t *tui) setAction() {
150166
cmd.Stdout = os.Stdout
151167
cmd.Stderr = os.Stderr
152168
cmd.Run()
153-
154169
return nil
155170

156171
case tcell.KeyBackspace, tcell.KeyBackspace2:
@@ -161,15 +176,30 @@ func (t *tui) setAction() {
161176
t.cliPane.setPrompt(t.cliPane.prompt)
162177

163178
t.stdinPane.reset()
179+
stdinCtx, stdinCancel := context.WithCancel(t.stdinPane.ctx)
164180
t.stdoutPane.reset()
181+
stdoutCtx, stdoutCancel := context.WithCancel(t.stdoutPane.ctx)
165182

166183
p := t.cliPane.prompt
184+
t.cliPane.syncUpdate(func() {
185+
t.cliPane.wg.Add(1)
186+
})
167187
go func() {
188+
defer t.cliPane.wg.Done()
189+
defer stdinCancel()
190+
defer stdoutCancel()
168191
if p == "" {
169192
t.stdinPane.setData(stdinBytes)
170193
} else {
171-
t.stdinPane.execCommand(p, stdinBytes)
194+
t.stdinPane.execCommand(stdinCtx, p, stdinBytes)
172195
}
196+
var text string
197+
t.QueueUpdate(func() {
198+
text = t.cliPane.GetText()
199+
})
200+
t.stdinPane.syncUpdate(func() {
201+
t.stdoutPane.execCommand(stdoutCtx, text, t.stdinPane.data)
202+
})
173203
}()
174204
return nil
175205
}
@@ -181,20 +211,21 @@ func (t *tui) setAction() {
181211
t.cliPane.addPrompt()
182212

183213
t.stdinPane.reset()
214+
stdinCtx, stdinCancel := context.WithCancel(t.stdinPane.ctx)
184215
t.stdoutPane.reset()
185216

186217
p := t.cliPane.prompt
218+
t.cliPane.syncUpdate(func() {
219+
t.cliPane.wg.Add(1)
220+
})
187221
go func() {
222+
defer t.cliPane.wg.Done()
223+
defer stdinCancel()
188224
if p == "" {
189225
t.stdinPane.setData(stdinBytes)
190226
} else {
191-
t.stdinPane.execCommand(p, stdinBytes)
227+
t.stdinPane.execCommand(stdinCtx, p, stdinBytes)
192228
}
193-
var text string
194-
t.QueueUpdate(func() {
195-
text = t.cliPane.GetText()
196-
})
197-
t.stdoutPane.execCommand(text, t.stdinPane.data)
198229
}()
199230
return nil
200231
case ' ':
@@ -210,6 +241,8 @@ type cliPane struct {
210241
symbol string
211242
prompt string
212243
skip bool
244+
wg sync.WaitGroup
245+
mu sync.Mutex
213246
}
214247

215248
func newCliPane() *cliPane {
@@ -231,6 +264,12 @@ func newCliPane() *cliPane {
231264
return c
232265
}
233266

267+
func (c *cliPane) syncUpdate(fn func()) {
268+
c.mu.Lock()
269+
defer c.mu.Unlock()
270+
fn()
271+
}
272+
234273
func (c *cliPane) skipHandler() {
235274
c.skip = true
236275
}
@@ -318,26 +357,23 @@ func (si *stdinViewPane) setData(inputBytes []byte) {
318357
si.syncUpdate(func() {
319358
si.data = make([]byte, len(inputBytes))
320359
copy(si.data, inputBytes)
321-
io.Copy(w, bytes.NewReader(inputBytes))
322-
})
360+
}) //
361+
io.Copy(w, bytes.NewReader(inputBytes))
323362
}
324363

325-
func (si *stdinViewPane) execCommand(text string, inputBytes []byte) {
364+
func (si *stdinViewPane) execCommand(ctx context.Context, text string, inputBytes []byte) {
326365
_data := new(bytes.Buffer)
327366
tt := newTextLineTransformer()
328367
w := transform.NewWriter(si, tt)
329368
mw := io.MultiWriter(w, _data)
330369

331-
ctx, cancel := context.WithCancel(si.ctx)
332-
defer cancel()
333-
334370
cmd := exec.CommandContext(ctx, shell, "-c", text)
335371

336-
si.syncUpdate(func() {
337-
cmd.Stdin = bytes.NewReader(inputBytes)
338-
cmd.Stdout = mw
372+
cmd.Stdin = bytes.NewReader(inputBytes)
373+
cmd.Stdout = mw
339374

340-
cmd.Run()
375+
cmd.Run()
376+
si.syncUpdate(func() {
341377
si.data = _data.Bytes()
342378
})
343379
}
@@ -354,22 +390,17 @@ func newStdoutViewPane() *stdoutViewPane {
354390
return so
355391
}
356392

357-
func (so *stdoutViewPane) execCommand(text string, inputBytes []byte) {
393+
func (so *stdoutViewPane) execCommand(ctx context.Context, text string, inputBytes []byte) {
358394
tt := newTextLineTransformer()
359395
w := transform.NewWriter(so, tt)
360396

361-
ctx, cancel := context.WithCancel(so.ctx)
362-
defer cancel()
363-
364397
cmd := exec.CommandContext(ctx, shell, "-c", text)
365398

366-
so.syncUpdate(func() {
367-
cmd.Stdin = bytes.NewReader(inputBytes)
368-
cmd.Stdout = w
369-
cmd.Stderr = w
399+
cmd.Stdin = bytes.NewReader(inputBytes)
400+
cmd.Stdout = w
401+
cmd.Stderr = w
370402

371-
cmd.Run()
372-
})
403+
cmd.Run()
373404
}
374405

375406
type textLineTransformer struct {

main_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"bytes"
5+
"context"
56
"fmt"
67
"io"
78
"strings"
@@ -104,7 +105,9 @@ func TestExecCommandStdin(t *testing.T) {
104105
}
105106
for _, tc := range cases {
106107
si := newStdinViewPane()
107-
si.execCommand(tc.cmd, []byte(tc.stdin))
108+
ctx, cancel := context.WithCancel(context.Background())
109+
defer cancel()
110+
si.execCommand(ctx, tc.cmd, []byte(tc.stdin))
108111
if !(bytes.Equal(si.data, []byte(tc.result))) {
109112
r := strings.Replace(fmt.Sprintf(`result: "%s"`, string(si.data)), "\n", "\\n", -1)
110113
e := strings.Replace(fmt.Sprintf(`expected: "%s"`, tc.result), "\n", "\\n", -1)
@@ -135,7 +138,9 @@ func TestExecCommandStdout(t *testing.T) {
135138
}
136139
for _, tc := range cases {
137140
so := newStdoutViewPane()
138-
so.execCommand(tc.cmd, []byte(tc.stdin))
141+
ctx, cancel := context.WithCancel(context.Background())
142+
defer cancel()
143+
so.execCommand(ctx, tc.cmd, []byte(tc.stdin))
139144
if !(so.GetText(true) == tc.result) {
140145
r := strings.Replace(fmt.Sprintf(`result: "%s"`, so.GetText(true)), "\n", "\\n", -1)
141146
e := strings.Replace(fmt.Sprintf(`expected: "%s"`, tc.result), "\n", "\\n", -1)

0 commit comments

Comments
 (0)