Skip to content

Commit dcb4c3d

Browse files
committed
Fix race in reload action
Fix #4070
1 parent 82ebcd9 commit dcb4c3d

File tree

2 files changed

+60
-36
lines changed

2 files changed

+60
-36
lines changed

src/core.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ func Run(opts *Options) (int, error) {
299299
itemIndex = 0
300300
inputRevision.bumpMajor()
301301
header = make([]string, 0, opts.HeaderLines)
302-
go reader.restart(command, environ)
302+
reader.restart(command, environ)
303303
}
304304

305305
exitCode := ExitOk

src/reader.go

Lines changed: 59 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,24 @@ type Reader struct {
2525
event int32
2626
finChan chan bool
2727
mutex sync.Mutex
28-
exec *exec.Cmd
29-
execOut io.ReadCloser
28+
termFunc func()
3029
command *string
31-
killed bool
3230
wait bool
3331
}
3432

3533
// NewReader returns new Reader object
3634
func NewReader(pusher func([]byte) bool, eventBox *util.EventBox, executor *util.Executor, delimNil bool, wait bool) *Reader {
37-
return &Reader{pusher, executor, eventBox, delimNil, int32(EvtReady), make(chan bool, 1), sync.Mutex{}, nil, nil, nil, false, wait}
35+
return &Reader{
36+
pusher,
37+
executor,
38+
eventBox,
39+
delimNil,
40+
int32(EvtReady),
41+
make(chan bool, 1),
42+
sync.Mutex{},
43+
func() { os.Stdin.Close() },
44+
nil,
45+
wait}
3846
}
3947

4048
func (r *Reader) startEventPoller() {
@@ -61,6 +69,10 @@ func (r *Reader) startEventPoller() {
6169
}()
6270
}
6371

72+
func (r *Reader) wasKilled() bool {
73+
return r.termFunc == nil
74+
}
75+
6476
func (r *Reader) fin(success bool) {
6577
atomic.StoreInt32(&r.event, int32(EvtReadFin))
6678
if r.wait {
@@ -69,7 +81,7 @@ func (r *Reader) fin(success bool) {
6981

7082
r.mutex.Lock()
7183
ret := r.command
72-
if success || r.killed {
84+
if success || r.wasKilled() {
7385
ret = nil
7486
}
7587
r.mutex.Unlock()
@@ -79,22 +91,27 @@ func (r *Reader) fin(success bool) {
7991

8092
func (r *Reader) terminate() {
8193
r.mutex.Lock()
82-
r.killed = true
83-
if r.exec != nil && r.exec.Process != nil {
84-
r.execOut.Close()
85-
util.KillCommand(r.exec)
86-
} else {
87-
os.Stdin.Close()
94+
if r.termFunc != nil {
95+
r.termFunc()
96+
r.termFunc = nil
8897
}
8998
r.mutex.Unlock()
9099
}
91100

92101
func (r *Reader) restart(command commandSpec, environ []string) {
93102
r.event = int32(EvtReady)
94103
r.startEventPoller()
95-
success := r.readFromCommand(command.command, environ)
96-
r.fin(success)
97-
removeFiles(command.tempFiles)
104+
105+
r.mutex.Lock()
106+
defer r.mutex.Unlock()
107+
108+
if exec, execOut := r.startCommand(command.command, environ); exec != nil {
109+
go func() {
110+
success := r.feedCommandOutput(exec, execOut)
111+
r.fin(success)
112+
removeFiles(command.tempFiles)
113+
}()
114+
}
98115
}
99116

100117
func (r *Reader) readChannel(inputChan chan string) bool {
@@ -249,7 +266,6 @@ func trimPath(path string) string {
249266
}
250267

251268
func (r *Reader) readFiles(root string, opts walkerOpts, ignores []string) bool {
252-
r.killed = false
253269
conf := fastwalk.Config{
254270
Follow: opts.follow,
255271
// Use forward slashes when running a Windows binary under WSL or MSYS
@@ -280,39 +296,47 @@ func (r *Reader) readFiles(root string, opts walkerOpts, ignores []string) bool
280296
}
281297
r.mutex.Lock()
282298
defer r.mutex.Unlock()
283-
if r.killed {
299+
if r.wasKilled() {
284300
return context.Canceled
285301
}
286302
return nil
287303
}
288304
return fastwalk.Walk(&conf, root, fn) == nil
289305
}
290306

291-
func (r *Reader) readFromCommand(command string, environ []string) bool {
292-
r.mutex.Lock()
293-
r.killed = false
307+
// Should be called with the mutex held
308+
func (r *Reader) startCommand(command string, environ []string) (*exec.Cmd, io.ReadCloser) {
309+
r.termFunc = nil
294310
r.command = &command
295-
r.exec = r.executor.ExecCommand(command, true)
311+
exec := r.executor.ExecCommand(command, true)
296312
if environ != nil {
297-
r.exec.Env = environ
313+
exec.Env = environ
298314
}
299-
300-
var err error
301-
r.execOut, err = r.exec.StdoutPipe()
302-
if err != nil {
303-
r.exec = nil
304-
r.mutex.Unlock()
305-
return false
315+
execOut, err := exec.StdoutPipe()
316+
if err != nil || exec.Start() != nil {
317+
return nil, nil
306318
}
307319

308-
err = r.exec.Start()
309-
if err != nil {
310-
r.exec = nil
311-
r.mutex.Unlock()
312-
return false
320+
// Function to call to terminate the running command
321+
r.termFunc = func() {
322+
execOut.Close()
323+
util.KillCommand(exec)
313324
}
314325

326+
return exec, execOut
327+
}
328+
329+
func (r *Reader) feedCommandOutput(exec *exec.Cmd, execOut io.ReadCloser) bool {
330+
r.feed(execOut)
331+
return exec.Wait() == nil
332+
}
333+
334+
func (r *Reader) readFromCommand(command string, environ []string) bool {
335+
r.mutex.Lock()
336+
exec, execOut := r.startCommand(command, environ)
315337
r.mutex.Unlock()
316-
r.feed(r.execOut)
317-
return r.exec.Wait() == nil
338+
if exec == nil {
339+
return false
340+
}
341+
return r.feedCommandOutput(exec, execOut)
318342
}

0 commit comments

Comments
 (0)